summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:42 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:28:42 -0800
commit88b607994a148f4af5bffee163e39ce8296750c6 (patch)
treefa249ff843e976cf034f2029437d3362a8396321
parent05806d7af62e07c6225b2e7103a1b115ecf6c9ad (diff)
downloadbuild-88b607994a148f4af5bffee163e39ce8296750c6.tar.gz
build-88b607994a148f4af5bffee163e39ce8296750c6.tar.bz2
build-88b607994a148f4af5bffee163e39ce8296750c6.zip
auto import from //depot/cupcake/@135843
-rw-r--r--buildspec.mk.default100
-rw-r--r--cleanspec.mk74
-rw-r--r--core/Makefile1163
-rw-r--r--core/apicheck_msg_current.txt18
-rw-r--r--core/apicheck_msg_last.txt7
-rw-r--r--core/armelf.x198
-rw-r--r--core/armelf.xsc201
-rw-r--r--core/armelflib.x164
-rw-r--r--core/base_rules.mk441
-rw-r--r--core/binary.mk403
-rw-r--r--core/build_id.mk32
-rwxr-xr-xcore/checktree113
-rw-r--r--core/cleanbuild.mk204
-rw-r--r--core/clear_vars.mk91
-rw-r--r--core/combo/darwin-x86.mk97
-rw-r--r--core/combo/javac.mk37
-rw-r--r--core/combo/linux-arm.mk154
-rw-r--r--core/combo/linux-x86.mk33
-rw-r--r--core/combo/select.mk81
-rw-r--r--core/combo/target_linux-x86.mk129
-rw-r--r--core/combo/windows-x86.mk53
-rw-r--r--core/config.mk310
-rw-r--r--core/copy_headers.mk23
-rw-r--r--core/definitions.mk1488
-rw-r--r--core/device.mk76
-rw-r--r--core/distdir.mk75
-rw-r--r--core/droiddoc.mk158
-rw-r--r--core/dynamic_binary.mk144
-rw-r--r--core/envsetup.mk333
-rw-r--r--core/executable.mk28
-rw-r--r--core/filter_symbols.sh25
-rwxr-xr-xcore/find-jdk-tools-jar.sh10
-rw-r--r--core/host_executable.mk20
-rw-r--r--core/host_java_library.mk29
-rw-r--r--core/host_prebuilt.mk18
-rw-r--r--core/host_shared_library.mk29
-rw-r--r--core/host_static_library.mk23
-rw-r--r--core/java.mk178
-rw-r--r--core/java_library.mk47
-rw-r--r--core/key_char_map.mk27
-rw-r--r--core/main.mk656
-rw-r--r--core/multi_prebuilt.mk111
-rw-r--r--core/node_fns.mk238
-rw-r--r--core/notice_files.mk65
-rw-r--r--core/package.mk273
-rw-r--r--core/pathmap.mk95
-rw-r--r--core/prebuilt.mk37
-rw-r--r--core/prelink-linux-arm.map139
-rwxr-xr-xcore/process_wrapper.sh17
-rw-r--r--core/process_wrapper_gdb.cmds1
-rwxr-xr-xcore/process_wrapper_gdb.sh17
-rw-r--r--core/product.mk156
-rw-r--r--core/product_config.mk244
-rw-r--r--core/raw_executable.mk26
-rw-r--r--core/raw_static_library.mk5
-rw-r--r--core/root.mk3
-rw-r--r--core/shared_library.mk32
-rw-r--r--core/static_java_library.mk25
-rw-r--r--core/static_library.mk29
-rw-r--r--core/tasks/apicheck.mk76
-rw-r--r--core/tasks/cts.mk95
-rw-r--r--core/tasks/localize.mk47
-rw-r--r--core/version_defaults.mk84
-rw-r--r--envsetup.sh1037
-rw-r--r--libs/host/Android.mk26
-rw-r--r--libs/host/CopyFile.c626
-rw-r--r--libs/host/Directories.cpp42
-rw-r--r--libs/host/include/host/CopyFile.h30
-rw-r--r--libs/host/include/host/Directories.h10
-rw-r--r--libs/host/include/host/pseudolocalize.h9
-rw-r--r--libs/host/list.java35
-rw-r--r--libs/host/pseudolocalize.cpp119
-rw-r--r--target/board/Android.mk54
-rw-r--r--target/board/emulator/AndroidBoard.mk10
-rw-r--r--target/board/emulator/BoardConfig.mk9
-rw-r--r--target/board/emulator/README.txt10
-rw-r--r--target/board/emulator/tuttle2.kcm66
-rw-r--r--target/board/emulator/tuttle2.kl74
-rw-r--r--target/board/generic/AndroidBoard.mk10
-rw-r--r--target/board/generic/BoardConfig.mk11
-rw-r--r--target/board/generic/README.txt9
-rw-r--r--target/board/generic/system.prop6
-rw-r--r--target/board/generic/tuttle2.kcm66
-rw-r--r--target/board/generic/tuttle2.kl74
-rw-r--r--target/board/sim/AndroidBoard.mk0
-rw-r--r--target/board/sim/BoardConfig.mk22
-rw-r--r--target/product/AndroidProducts.mk33
-rw-r--r--target/product/core.mk23
-rw-r--r--target/product/generic.mk26
-rwxr-xr-xtarget/product/generic_with_google.mk13
-rw-r--r--target/product/min_dev.mk19
-rw-r--r--target/product/sdk.mk29
-rw-r--r--target/product/security/README38
-rw-r--r--target/product/security/media.pk8bin0 -> 1216 bytes
-rw-r--r--target/product/security/media.x509.pem27
-rw-r--r--target/product/security/mkkey.sh15
-rw-r--r--target/product/security/platform.pk8bin0 -> 1216 bytes
-rw-r--r--target/product/security/platform.x509.pem27
-rw-r--r--target/product/security/shared.pk8bin0 -> 1218 bytes
-rw-r--r--target/product/security/shared.x509.pem27
-rw-r--r--target/product/security/testkey.pk8bin0 -> 1217 bytes
-rw-r--r--target/product/security/testkey.x509.pem27
-rw-r--r--target/product/sim.mk9
-rw-r--r--tools/acp/Android.mk26
-rw-r--r--tools/acp/README40
-rw-r--r--tools/acp/acp.c252
-rw-r--r--tools/apicheck/Android.mk44
-rw-r--r--tools/apicheck/etc/apicheck46
-rw-r--r--tools/apicheck/src/Android.mk28
-rw-r--r--tools/apicheck/src/MANIFEST.mf2
-rw-r--r--tools/apicheck/src/com/android/apicheck/AbstractMethodInfo.java24
-rw-r--r--tools/apicheck/src/com/android/apicheck/ApiCheck.java253
-rw-r--r--tools/apicheck/src/com/android/apicheck/ApiInfo.java81
-rw-r--r--tools/apicheck/src/com/android/apicheck/ClassInfo.java282
-rw-r--r--tools/apicheck/src/com/android/apicheck/ConstructorInfo.java147
-rw-r--r--tools/apicheck/src/com/android/apicheck/Errors.java156
-rw-r--r--tools/apicheck/src/com/android/apicheck/FieldInfo.java160
-rw-r--r--tools/apicheck/src/com/android/apicheck/MethodInfo.java209
-rw-r--r--tools/apicheck/src/com/android/apicheck/PackageInfo.java78
-rw-r--r--tools/apicheck/src/com/android/apicheck/ParameterInfo.java35
-rw-r--r--tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java122
-rw-r--r--tools/applypatch/Android.mk29
-rw-r--r--tools/applypatch/applypatch.c457
-rw-r--r--tools/applypatch/applypatch.h53
-rwxr-xr-xtools/applypatch/applypatch.sh272
-rw-r--r--tools/applypatch/bsdiff.c232
-rw-r--r--tools/applypatch/freecache.c172
-rw-r--r--tools/applypatch/testdata/new.filebin0 -> 1388877 bytes
-rw-r--r--tools/applypatch/testdata/old.filebin0 -> 1348051 bytes
-rw-r--r--tools/applypatch/testdata/patch.bsdiffbin0 -> 57476 bytes
-rw-r--r--tools/apriori/Android.mk61
-rw-r--r--tools/apriori/apriori.c2601
-rw-r--r--tools/apriori/apriori.h14
-rw-r--r--tools/apriori/cmdline.c186
-rw-r--r--tools/apriori/cmdline.h21
-rw-r--r--tools/apriori/common.h28
-rw-r--r--tools/apriori/debug.c38
-rw-r--r--tools/apriori/debug.h88
-rw-r--r--tools/apriori/hash.c27
-rw-r--r--tools/apriori/hash.h14
-rw-r--r--tools/apriori/main.c229
-rw-r--r--tools/apriori/prelink_info.c106
-rw-r--r--tools/apriori/prelink_info.h9
-rw-r--r--tools/apriori/prelinkmap.c139
-rw-r--r--tools/apriori/prelinkmap.h10
-rw-r--r--tools/apriori/rangesort.c317
-rw-r--r--tools/apriori/rangesort.h105
-rw-r--r--tools/apriori/source.c18
-rw-r--r--tools/apriori/source.h121
-rwxr-xr-xtools/apriori/tweak.h15
-rw-r--r--tools/atree/Android.mk20
-rw-r--r--tools/atree/atree.cpp301
-rw-r--r--tools/atree/files.cpp427
-rw-r--r--tools/atree/files.h39
-rw-r--r--tools/atree/fs.cpp143
-rw-r--r--tools/atree/fs.h12
-rw-r--r--tools/atree/options.h14
-rw-r--r--tools/bin2asm/Android.mk24
-rw-r--r--tools/bin2asm/data52
-rw-r--r--tools/bin2asm/icudata.c72
-rwxr-xr-xtools/buildinfo.sh34
-rwxr-xr-xtools/check_builds.sh67
-rw-r--r--tools/check_prereq/Android.mk30
-rw-r--r--tools/check_prereq/check_prereq.c46
-rwxr-xr-xtools/compare_fileslist.py106
-rw-r--r--tools/dexpreopt/Android.mk38
-rw-r--r--tools/dexpreopt/Config.mk146
-rw-r--r--tools/dexpreopt/afar/Android.mk29
-rw-r--r--tools/dexpreopt/afar/main.c247
-rw-r--r--tools/dexpreopt/dexopt-wrapper/Android.mk36
-rw-r--r--tools/dexpreopt/dexopt-wrapper/DexOptWrapper.cpp173
-rwxr-xr-xtools/dexpreopt/dexpreopt.py981
-rw-r--r--tools/dexpreopt/geninitrc.awk62
-rw-r--r--tools/droiddoc/Android.mk18
-rw-r--r--tools/droiddoc/src/Android.mk69
-rw-r--r--tools/droiddoc/src/AnnotationInstanceInfo.java59
-rw-r--r--tools/droiddoc/src/AnnotationValueInfo.java79
-rw-r--r--tools/droiddoc/src/AttrTagInfo.java140
-rw-r--r--tools/droiddoc/src/AttributeInfo.java100
-rw-r--r--tools/droiddoc/src/ClassInfo.java1463
-rw-r--r--tools/droiddoc/src/ClearPage.java226
-rw-r--r--tools/droiddoc/src/Comment.java394
-rw-r--r--tools/droiddoc/src/ContainerInfo.java21
-rw-r--r--tools/droiddoc/src/Converter.java744
-rw-r--r--tools/droiddoc/src/DocFile.java137
-rw-r--r--tools/droiddoc/src/DocInfo.java58
-rw-r--r--tools/droiddoc/src/DroidDoc.java1342
-rw-r--r--tools/droiddoc/src/Errors.java143
-rw-r--r--tools/droiddoc/src/FieldInfo.java315
-rwxr-xr-xtools/droiddoc/src/Hierarchy.java155
-rw-r--r--tools/droiddoc/src/InheritedTags.java27
-rw-r--r--tools/droiddoc/src/KeywordEntry.java51
-rw-r--r--tools/droiddoc/src/LinkReference.java440
-rw-r--r--tools/droiddoc/src/LiteralTagInfo.java31
-rw-r--r--tools/droiddoc/src/MemberInfo.java154
-rw-r--r--tools/droiddoc/src/MethodInfo.java645
-rw-r--r--tools/droiddoc/src/NavTree.java142
-rw-r--r--tools/droiddoc/src/PackageInfo.java185
-rw-r--r--tools/droiddoc/src/ParamTagInfo.java95
-rw-r--r--tools/droiddoc/src/ParameterInfo.java72
-rwxr-xr-xtools/droiddoc/src/ParsedTagInfo.java61
-rw-r--r--tools/droiddoc/src/Proofread.java178
-rw-r--r--tools/droiddoc/src/SampleCode.java161
-rw-r--r--tools/droiddoc/src/SampleTagInfo.java288
-rw-r--r--tools/droiddoc/src/Scoped.java23
-rw-r--r--tools/droiddoc/src/SeeTagInfo.java79
-rw-r--r--tools/droiddoc/src/Sorter.java32
-rw-r--r--tools/droiddoc/src/SourcePositionInfo.java94
-rw-r--r--tools/droiddoc/src/Stubs.java988
-rw-r--r--tools/droiddoc/src/TagInfo.java100
-rw-r--r--tools/droiddoc/src/TextTagInfo.java21
-rw-r--r--tools/droiddoc/src/ThrowsTagInfo.java83
-rw-r--r--tools/droiddoc/src/TodoFile.java195
-rw-r--r--tools/droiddoc/src/TypeInfo.java289
-rw-r--r--tools/droiddoc/templates-sdk/assets-sdk/placeholder0
-rw-r--r--tools/droiddoc/templates-sdk/customization.cs121
-rw-r--r--tools/droiddoc/templates-sdk/data.hdf4
-rw-r--r--tools/droiddoc/templates-sdk/devdoc-nav.cs66
-rw-r--r--tools/droiddoc/templates-sdk/sdkpage.cs108
-rw-r--r--tools/droiddoc/templates/assets/android-developer-core.css909
-rw-r--r--tools/droiddoc/templates/assets/android-developer-docs-devguide.css19
-rw-r--r--tools/droiddoc/templates/assets/android-developer-docs.css1083
-rw-r--r--tools/droiddoc/templates/assets/android-developer-docs.js330
-rw-r--r--tools/droiddoc/templates/assets/carousel.js295
-rw-r--r--tools/droiddoc/templates/assets/images/android-developers-logo.pngbin0 -> 3195 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/android_wrench.pngbin0 -> 3633 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/arrow_left_off.jpgbin0 -> 592 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/arrow_left_on.jpgbin0 -> 692 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/arrow_right_off.jpgbin0 -> 592 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/arrow_right_on.jpgbin0 -> 692 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/bg_community_leftDiv.jpgbin0 -> 10692 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/bg_fade.jpgbin0 -> 300 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/bg_images_sprite.pngbin0 -> 2008 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/bg_logo.pngbin0 -> 4299 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/body-gradient-tab.pngbin0 -> 192 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/body-gradient.pngbin0 -> 146 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/developers-logo.pngbin0 -> 10155 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/grad-rule-qv.pngbin0 -> 249 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/IO-logo.pngbin0 -> 3673 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/home/bg_home_bottom.jpgbin0 -> 2180 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/bg_home_middle.pngbin0 -> 3614 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.pngbin0 -> 3413 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/home/devphone-large.pngbin0 -> 14140 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/home/devphone-small.pngbin0 -> 3910 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/maps-large.pngbin0 -> 39756 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/maps-small.pngbin0 -> 3424 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/market-large.pngbin0 -> 5752 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/market-small.pngbin0 -> 3134 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/sdk-large.pngbin0 -> 1616 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/home/sdk-small.pngbin0 -> 2381 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/hr_gray_main.jpgbin0 -> 378 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/hr_gray_side.jpgbin0 -> 344 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_contribute.jpgbin0 -> 1021 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_download.jpgbin0 -> 1192 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_download2.jpgbin0 -> 653 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_market.jpgbin0 -> 1069 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_robot.jpgbin0 -> 638 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/icon_world.jpgbin0 -> 511 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/left_off.jpgbin0 -> 592 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/left_on.jpgbin0 -> 692 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/logo_breadcrumbz.jpgbin0 -> 3311 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/preliminary.pngbin0 -> 1432 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-e.gifbin0 -> 2713 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-e2.gifbin0 -> 2680 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-eg.gifbin0 -> 3075 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-s.gifbin0 -> 2617 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-s2.gifbin0 -> 2618 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/resizable-sg.gifbin0 -> 3057 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/right_off.jpgbin0 -> 592 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/right_on.jpgbin0 -> 692 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/sidenav-rule.pngbin0 -> 258 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_1.jpgbin0 -> 3060 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_2.jpgbin0 -> 3736 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_3.jpgbin0 -> 3151 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_large_1.jpgbin0 -> 19240 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_large_2.jpgbin0 -> 20188 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_large_3.jpgbin0 -> 20202 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_off.jpgbin0 -> 676 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/slide_on.jpgbin0 -> 733 bytes
-rwxr-xr-xtools/droiddoc/templates/assets/images/spacer.gifbin0 -> 96 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/triangle-closed-small.pngbin0 -> 166 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/triangle-closed.pngbin0 -> 3614 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/triangle-opened-small.pngbin0 -> 170 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/triangle-opened.pngbin0 -> 3612 bytes
-rw-r--r--tools/droiddoc/templates/assets/images/video-droid.pngbin0 -> 1614 bytes
-rw-r--r--tools/droiddoc/templates/assets/jdiff_logo.gifbin0 -> 390 bytes
-rw-r--r--tools/droiddoc/templates/assets/jquery-history.js78
-rwxr-xr-xtools/droiddoc/templates/assets/jquery-resizable.min.js94
-rw-r--r--tools/droiddoc/templates/assets/navtree.js179
-rw-r--r--tools/droiddoc/templates/assets/search_autocomplete.js173
-rw-r--r--tools/droiddoc/templates/assets/style.css316
-rw-r--r--tools/droiddoc/templates/assets/triangle-none.gifbin0 -> 240 bytes
-rw-r--r--tools/droiddoc/templates/class.cs624
-rw-r--r--tools/droiddoc/templates/classes.cs41
-rw-r--r--tools/droiddoc/templates/customization.cs24
-rw-r--r--tools/droiddoc/templates/docpage.cs42
-rw-r--r--tools/droiddoc/templates/doctype.cs1
-rw-r--r--tools/droiddoc/templates/footer.cs19
-rw-r--r--tools/droiddoc/templates/head_tag.cs37
-rw-r--r--tools/droiddoc/templates/header.cs3
-rw-r--r--tools/droiddoc/templates/hierarchy.cs68
-rw-r--r--tools/droiddoc/templates/index.cs8
-rw-r--r--tools/droiddoc/templates/keywords.cs37
-rw-r--r--tools/droiddoc/templates/lists.cs5
-rw-r--r--tools/droiddoc/templates/macros.cs333
-rw-r--r--tools/droiddoc/templates/navtree_data.cs4
-rw-r--r--tools/droiddoc/templates/nosidenavpage.cs23
-rw-r--r--tools/droiddoc/templates/package-descr.cs32
-rw-r--r--tools/droiddoc/templates/package-list.cs2
-rw-r--r--tools/droiddoc/templates/package.cs52
-rw-r--r--tools/droiddoc/templates/packages.cs38
-rw-r--r--tools/droiddoc/templates/sample.cs34
-rw-r--r--tools/droiddoc/templates/sampleindex.cs48
-rw-r--r--tools/droiddoc/templates/todo.cs99
-rw-r--r--tools/droiddoc/templates/trailer.cs11
-rw-r--r--tools/droiddoc/test/generics/Android.mk28
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java20
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/Adapter.java20
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java21
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/Bar.java22
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/Foo.java28
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/FooBar.java51
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/Iface.java20
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java20
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java20
-rw-r--r--tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java21
-rw-r--r--tools/droiddoc/test/stubs/Android.mk29
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java8
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java9
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java27
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java7
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java33
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java14
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java4
-rw-r--r--tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java5
-rw-r--r--tools/droiddoc/test/stubs/func.sh68
-rwxr-xr-xtools/droiddoc/test/stubs/run.sh39
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java30
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java23
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java60
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java21
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/Types.java72
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java41
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java21
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java26
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java23
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java25
-rw-r--r--tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java22
-rwxr-xr-xtools/dump-package-stats152
-rwxr-xr-xtools/fileslist.py45
-rwxr-xr-xtools/findleaves.sh109
-rwxr-xr-xtools/fixlinebreaks.sh16
-rw-r--r--tools/fs_config/Android.mk27
-rw-r--r--tools/fs_config/fs_config.c69
-rw-r--r--tools/fs_get_stats/Android.mk9
-rw-r--r--tools/fs_get_stats/fs_get_stats.c64
-rwxr-xr-xtools/iself/Android.mk23
-rw-r--r--tools/iself/debug.h90
-rw-r--r--tools/iself/iself.c36
-rwxr-xr-xtools/isprelinked/Android.mk40
-rw-r--r--tools/isprelinked/common.h28
-rw-r--r--tools/isprelinked/debug.c37
-rw-r--r--tools/isprelinked/debug.h88
-rw-r--r--tools/isprelinked/isprelinked.c89
-rw-r--r--tools/isprelinked/prelink_info.c71
-rw-r--r--tools/isprelinked/prelink_info.h8
-rw-r--r--tools/kcm/Android.mk15
-rw-r--r--tools/kcm/kcm.cpp421
-rw-r--r--tools/lsd/Android.mk43
-rw-r--r--tools/lsd/cmdline.c130
-rw-r--r--tools/lsd/cmdline.h13
-rw-r--r--tools/lsd/common.h12
-rw-r--r--tools/lsd/debug.c39
-rw-r--r--tools/lsd/debug.h93
-rw-r--r--tools/lsd/hash.c29
-rw-r--r--tools/lsd/hash.h14
-rw-r--r--tools/lsd/lsd.c777
-rw-r--r--tools/lsd/lsd.h10
-rw-r--r--tools/lsd/main.c67
-rwxr-xr-xtools/mktarball.sh50
-rwxr-xr-xtools/print_module_licenses.sh2
-rw-r--r--tools/rgb2565/Android.mk17
-rw-r--r--tools/rgb2565/to565.c143
-rw-r--r--tools/signapk/Android.mk27
-rw-r--r--tools/signapk/SignApk.java390
-rw-r--r--tools/signapk/SignApk.mf1
-rwxr-xr-xtools/signapk/test/run30
-rw-r--r--tools/soslim/Android.mk49
-rw-r--r--tools/soslim/cmdline.c141
-rw-r--r--tools/soslim/cmdline.h16
-rw-r--r--tools/soslim/common.c35
-rw-r--r--tools/soslim/common.h49
-rw-r--r--tools/soslim/debug.c40
-rw-r--r--tools/soslim/debug.h88
-rw-r--r--tools/soslim/main.c360
-rw-r--r--tools/soslim/prelink_info.c106
-rw-r--r--tools/soslim/prelink_info.h9
-rw-r--r--tools/soslim/soslim.c528
-rw-r--r--tools/soslim/soslim.h32
-rw-r--r--tools/soslim/symfilter.c242
-rw-r--r--tools/soslim/symfilter.h50
-rw-r--r--tools/zipalign/Android.mk35
-rw-r--r--tools/zipalign/README.txt31
-rw-r--r--tools/zipalign/ZipAlign.cpp253
404 files changed, 43942 insertions, 0 deletions
diff --git a/buildspec.mk.default b/buildspec.mk.default
new file mode 100644
index 000000000..861bb0d9f
--- /dev/null
+++ b/buildspec.mk.default
@@ -0,0 +1,100 @@
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+######################################################################
+# This is a do-nothing template file. To use it, copy it to a file
+# named "buildspec.mk" in the root directory, and uncomment or change
+# the variables necessary for your desired configuration. The file
+# "buildspec.mk" should never be checked in to source control.
+######################################################################
+
+# Uncomment this if you want the simulator, otherwise, build for arm
+ifndef TARGET_SIMULATOR
+#TARGET_SIMULATOR:=true
+endif
+
+# Set this to debug or release if you care. Otherwise, it defaults to
+# release for arm and debug for the simulator.
+ifndef TARGET_BUILD_TYPE
+#TARGET_BUILD_TYPE:=release
+#TARGET_BUILD_TYPE:=debug
+endif
+
+# Uncomment this if you want the host tools built in debug mode. Otherwise
+# it defaults to release.
+ifndef HOST_BUILD_TYPE
+#HOST_BUILD_TYPE:=debug
+endif
+
+# Turn on debugging for selected modules. If DEBUG_MODULE_<module-name> is set
+# to a non-empty value, the appropriate HOST_/TARGET_CUSTOM_DEBUG_CFLAGS
+# will be added to LOCAL_CFLAGS when building the module.
+#DEBUG_MODULE_ModuleName:=true
+
+# Specify the extra CFLAGS to use when building a module whose
+# DEBUG_MODULE_ variable is set. Host and device flags are handled
+# separately.
+#HOST_CUSTOM_DEBUG_CFLAGS:=
+#TARGET_CUSTOM_DEBUG_CFLAGS:=
+
+# Choose a product to build for. Look in the products directory for ones
+# that work.
+ifndef TARGET_PRODUCT
+#TARGET_PRODUCT:=generic
+endif
+
+# Choose additional targets to always install, even when building
+# minimal targets like "make droid". This takes simple target names
+# like "Browser" or "MyApp", the names used by LOCAL_MODULE or
+# LOCAL_PACKAGE_NAME. Modules listed here will always be installed in
+# /system, even if they'd usually go in /data.
+ifndef CUSTOM_MODULES
+#CUSTOM_MODULES:=
+endif
+
+# Choose additional locales, like "en_US" or "it_IT", to add to any
+# built product. Any locales that appear in CUSTOM_LOCALES but not in
+# the locale list for the selected product will be added to the end
+# of PRODUCT_LOCALES.
+ifndef CUSTOM_LOCALES
+#CUSTOM_LOCALES:=
+endif
+
+# If you have a special place to put your ouput files, set this, otherwise
+# it goes to <build-root>/out
+#OUT_DIR:=/tmp/stuff
+
+# If you want to always set certain system properties, add them to this list.
+# E.g., "ADDITIONAL_BUILD_PROPERTIES += ro.prop1=5 prop2=value"
+# This mechanism does not currently support values containing spaces.
+#ADDITIONAL_BUILD_PROPERTIES +=
+
+# If you want to reduce the system.img size by several meg, and are willing to
+# lose access to CJK (and other) character sets, define NO_FALLBACK_FONT:=true
+ifndef NO_FALLBACK_FONT
+#NO_FALLBACK_FONT:=true
+endif
+
+# To enabled instrumentation in webcore based apps like gmail and
+# the browser, define WEBCORE_INSTRUMENTATION:=true
+#WEBCORE_INSTRUMENTATION:=true
+#endif
+
+# when the build system changes such that this file must be updated, this
+# variable will be changed. After you have modified this file with the new
+# changes (see buildspec.mk.default), update this to the new value from
+# buildspec.mk.default.
+BUILD_ENV_SEQUENCE_NUMBER := 9
diff --git a/cleanspec.mk b/cleanspec.mk
new file mode 100644
index 000000000..794c9cd50
--- /dev/null
+++ b/cleanspec.mk
@@ -0,0 +1,74 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Just bump this if you want to force a clean build.
+# **********************************************************************
+# WHEN DOING SO, DELETE ANY "add-clean-step" ENTRIES THAT HAVE PILED UP.
+# **********************************************************************
+#
+INTERNAL_CLEAN_BUILD_VERSION := 2
+#
+# ***********************************************************************
+# Do not touch INTERNAL_CLEAN_BUILD_VERSION if you've added a clean step!
+# ***********************************************************************
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/external/zlib/)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+$(call add-clean-step, rm -f $(PRODUCT_OUT)/system/etc/NOTICE.html)
+# Remove generated java files after CL 126153
+$(call add-clean-step, find $(OUT_DIR) -type f -name "*.java" -print0 | xargs -0 rm -f)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/sapphire/obj/SHARED_LIBRARIES/libhardware_legacy_intermediates/led)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/mountd)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/mountd.conf)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/Browser_intermediates)
+$(call add-clean-step, rm -f vendor/google/apps/Talk/res/drawable/%*)
+$(call add-clean-step, rm -rf $(OUT_DIR)/product/*/obj/SHARED_LIBRARIES/libandroid_runtime_intermediates/android_os_NetStat.o)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/libjni_andpyime_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/product/*/obj/SHARED_LIBRARIES/share)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/core/Makefile b/core/Makefile
new file mode 100644
index 000000000..9ba117aa5
--- /dev/null
+++ b/core/Makefile
@@ -0,0 +1,1163 @@
+# Put some miscellaneous rules here
+
+# Pick a reasonable string to use to identify files.
+ifneq "" "$(filter eng.%,$(BUILD_NUMBER))"
+ # BUILD_NUMBER has a timestamp in it, which means that
+ # it will change every time. Pick a stable value.
+ FILE_NAME_TAG := eng.$(USER)
+else
+ FILE_NAME_TAG := $(BUILD_NUMBER)
+endif
+
+# -----------------------------------------------------------------
+# Define rules to copy PRODUCT_COPY_FILES defined by the product.
+# PRODUCT_COPY_FILES contains words like <source file>:<dest file>.
+# <dest file> is relative to $(PRODUCT_OUT), so it should look like,
+# e.g., "system/etc/file.xml".
+$(foreach cf,$(PRODUCT_COPY_FILES), \
+ $(eval _w := $(subst :,$(space),$(cf))) \
+ $(eval _src := $(word 1,$(_w))) \
+ $(eval _dest := $(subst //,/,$(PRODUCT_OUT)/$(word 2,$(_w)))) \
+ $(eval $(call copy-one-file,$(_src),$(_dest))) \
+ $(eval ALL_DEFAULT_INSTALLED_MODULES += $(_dest)) \
+ )
+
+# -----------------------------------------------------------------
+# docs/index.html
+gen := $(OUT_DOCS)/index.html
+ALL_DOCS += $(gen)
+$(gen): frameworks/base/docs/docs-redirect-index.html
+ @mkdir -p $(dir $@)
+ @cp -f $< $@
+
+# -----------------------------------------------------------------
+# default.prop
+INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_ROOT_OUT)/default.prop
+ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_DEFAULT_PROP_TARGET)
+ADDITIONAL_DEFAULT_PROPERTIES := \
+ $(call collapse-pairs, $(ADDITIONAL_DEFAULT_PROPERTIES))
+
+$(INSTALLED_DEFAULT_PROP_TARGET):
+ @echo Target buildinfo: $@
+ @mkdir -p $(dir $@)
+ $(hide) echo "#" > $@; \
+ echo "# ADDITIONAL_DEFAULT_PROPERTIES" >> $@; \
+ echo "#" >> $@;
+ $(hide) $(foreach line,$(ADDITIONAL_DEFAULT_PROPERTIES), \
+ echo "$(line)" >> $@;)
+
+# -----------------------------------------------------------------
+# build.prop
+INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
+ALL_DEFAULT_INSTALLED_MODULES += $(INSTALLED_BUILD_PROP_TARGET)
+ADDITIONAL_BUILD_PROPERTIES := \
+ $(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))
+
+# A list of arbitrary tags describing the build configuration.
+# Force ":=" so we can use +=
+BUILD_VERSION_TAGS := $(BUILD_VERSION_TAGS)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ BUILD_VERSION_TAGS += debug
+endif
+# Apps are always signed with test keys, and may be re-signed in a post-build
+# step. If that happens, the "test-keys" tag will be removed by that step.
+BUILD_VERSION_TAGS += test-keys
+ifndef INCLUDE_TEST_OTA_KEYS
+ BUILD_VERSION_TAGS += ota-rel-keys
+endif
+BUILD_VERSION_TAGS := $(subst $(space),$(comma),$(sort $(BUILD_VERSION_TAGS)))
+
+# A human-readable string that descibes this build in detail.
+build_desc := $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT) $(PLATFORM_VERSION) $(BUILD_ID) $(BUILD_NUMBER) $(BUILD_VERSION_TAGS)
+$(INSTALLED_BUILD_PROP_TARGET): PRIVATE_BUILD_DESC := $(build_desc)
+
+# The string used to uniquely identify this build; used by the OTA server.
+ifeq (,$(strip $(BUILD_FINGERPRINT)))
+ BUILD_FINGERPRINT := $(PRODUCT_BRAND)/$(TARGET_PRODUCT)/$(TARGET_DEVICE)/$(TARGET_BOOTLOADER_BOARD_NAME):$(PLATFORM_VERSION)/$(BUILD_ID)/$(BUILD_NUMBER):$(TARGET_BUILD_VARIANT)/$(BUILD_VERSION_TAGS)
+endif
+ifneq ($(words $(BUILD_FINGERPRINT)),1)
+ $(error BUILD_FINGERPRINT cannot contain spaces: "$(BUILD_FINGERPRINT)")
+endif
+
+# Selects the first locale in the list given as the argument,
+# and splits it into language and region, which each may be
+# empty.
+define default-locale
+$(subst _, , $(firstword $(1)))
+endef
+
+# Selects the first locale in the list given as the argument
+# and returns the language (or the region)
+define default-locale-language
+$(word 2, 2, $(call default-locale, $(1)))
+endef
+define default-locale-region
+$(word 3, 3, $(call default-locale, $(1)))
+endef
+
+BUILDINFO_SH := build/tools/buildinfo.sh
+$(INSTALLED_BUILD_PROP_TARGET): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE)
+ @echo Target buildinfo: $@
+ @mkdir -p $(dir $@)
+ $(hide) TARGET_BUILD_TYPE="$(TARGET_BUILD_VARIANT)" \
+ TARGET_DEVICE="$(TARGET_DEVICE)" \
+ PRODUCT_NAME="$(TARGET_PRODUCT)" \
+ PRODUCT_BRAND="$(PRODUCT_BRAND)" \
+ PRODUCT_DEFAULT_LANGUAGE="$(call default-locale-language,$(PRODUCT_LOCALES))" \
+ PRODUCT_DEFAULT_REGION="$(call default-locale-region,$(PRODUCT_LOCALES))" \
+ PRODUCT_MODEL="$(PRODUCT_MODEL)" \
+ PRODUCT_MANUFACTURER="$(PRODUCT_MANUFACTURER)" \
+ PRIVATE_BUILD_DESC="$(PRIVATE_BUILD_DESC)" \
+ BUILD_ID="$(BUILD_ID)" \
+ BUILD_DISPLAY_ID="$(BUILD_DISPLAY_ID)" \
+ BUILD_NUMBER="$(BUILD_NUMBER)" \
+ PLATFORM_VERSION="$(PLATFORM_VERSION)" \
+ PLATFORM_SDK_VERSION="$(PLATFORM_SDK_VERSION)" \
+ BUILD_VERSION_TAGS="$(BUILD_VERSION_TAGS)" \
+ TARGET_BOOTLOADER_BOARD_NAME="$(TARGET_BOOTLOADER_BOARD_NAME)" \
+ BUILD_FINGERPRINT="$(BUILD_FINGERPRINT)" \
+ TARGET_BOARD_PLATFORM="$(TARGET_BOARD_PLATFORM)" \
+ bash $(BUILDINFO_SH) > $@
+ $(hide) if [ -f $(TARGET_DEVICE_DIR)/system.prop ]; then \
+ cat $(TARGET_DEVICE_DIR)/system.prop >> $@; \
+ fi
+ $(if $(ADDITIONAL_BUILD_PROPERTIES), \
+ $(hide) echo >> $@; \
+ echo "#" >> $@; \
+ echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
+ echo "#" >> $@; )
+ $(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
+ echo "$(line)" >> $@;)
+
+build_desc :=
+
+# -----------------------------------------------------------------
+# sdk-build.prop
+#
+# There are certain things in build.prop that we don't want to
+# ship with the sdk; remove them.
+
+# This must be a list of entire property keys followed by
+# "=" characters, without any internal spaces.
+sdk_build_prop_remove := \
+ ro.build.user= \
+ ro.build.host= \
+ ro.product.brand= \
+ ro.product.manufacturer= \
+ ro.product.device=
+# TODO: Remove this soon-to-be obsolete property
+sdk_build_prop_remove += ro.build.product=
+INSTALLED_SDK_BUILD_PROP_TARGET := $(PRODUCT_OUT)/sdk/sdk-build.prop
+$(INSTALLED_SDK_BUILD_PROP_TARGET): $(INSTALLED_BUILD_PROP_TARGET)
+ @echo SDK buildinfo: $@
+ @mkdir -p $(dir $@)
+ $(hide) grep -v "$(subst $(space),\|,$(strip \
+ $(sdk_build_prop_remove)))" $< > $@.tmp
+ $(hide) for x in $(sdk_build_prop_remove); do \
+ echo "$$x"generic >> $@.tmp; done
+ $(hide) mv $@.tmp $@
+
+# -----------------------------------------------------------------
+# package stats
+PACKAGE_STATS_FILE := $(PRODUCT_OUT)/package-stats.txt
+PACKAGES_TO_STAT := \
+ $(sort $(filter $(TARGET_OUT)/% $(TARGET_OUT_DATA)/%, \
+ $(filter %.jar %.apk, $(ALL_DEFAULT_INSTALLED_MODULES))))
+$(PACKAGE_STATS_FILE): $(PACKAGES_TO_STAT)
+ @echo Package stats: $@
+ @mkdir -p $(dir $@)
+ $(hide) rm -f $@
+ $(hide) build/tools/dump-package-stats $^ > $@
+
+.PHONY: package-stats
+package-stats: $(PACKAGE_STATS_FILE)
+
+# -----------------------------------------------------------------
+# Cert-to-package mapping. Used by the post-build signing tools.
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-apkcerts-$(FILE_NAME_TAG)
+intermediates := \
+ $(call intermediates-dir-for,PACKAGING,apkcerts)
+APKCERTS_FILE := $(intermediates)/$(name).txt
+# Depending on the built packages isn't exactly right,
+# but it should guarantee that the apkcerts file is rebuilt
+# if any packages change which certs they're signed with.
+all_built_packages := $(foreach p,$(PACKAGES),$(ALL_MODULES.$(p).BUILT))
+$(APKCERTS_FILE): $(all_built_packages)
+ @echo APK certs list: $@
+ @mkdir -p $(dir $@)
+ @rm -f $@
+ $(hide) $(foreach p,$(PACKAGES),\
+ echo 'name="$(p).apk" certificate="$(PACKAGES.$(p).CERTIFICATE)" \
+ private_key="$(PACKAGES.$(p).PRIVATE_KEY)"' >> $@;)
+
+.PHONY: apkcerts-list
+apkcerts-list: $(APKCERTS_FILE)
+
+# -----------------------------------------------------------------
+# module info file
+ifdef CREATE_MODULE_INFO_FILE
+ MODULE_INFO_FILE := $(PRODUCT_OUT)/module-info.txt
+ $(info Generating $(MODULE_INFO_FILE)...)
+ $(shell rm -f $(MODULE_INFO_FILE))
+ $(foreach m,$(ALL_MODULES), \
+ $(shell echo "NAME=\"$(m)\"" \
+ "PATH=\"$(strip $(ALL_MODULES.$(m).PATH))\"" \
+ "TAGS=\"$(strip $(filter-out _%,$(ALL_MODULES.$(m).TAGS)))\"" \
+ "BUILT=\"$(strip $(ALL_MODULES.$(m).BUILT))\"" \
+ "INSTALLED=\"$(strip $(ALL_MODULES.$(m).INSTALLED))\"" >> $(MODULE_INFO_FILE)))
+endif
+
+# Rules that need to be present for the simulator, even
+# if they don't do anything.
+.PHONY: systemimage
+systemimage:
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# #################################################################
+# Targets for boot/OS images
+# #################################################################
+
+# -----------------------------------------------------------------
+# the ramdisk
+INTERNAL_RAMDISK_FILES := $(filter $(TARGET_ROOT_OUT)/%, \
+ $(ALL_PREBUILT) \
+ $(ALL_COPIED_HEADERS) \
+ $(ALL_GENERATED_SOURCES) \
+ $(ALL_DEFAULT_INSTALLED_MODULES))
+
+INSTALLED_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
+$(INSTALLED_RAMDISK_TARGET): $(MKBOOTFS) $(INTERNAL_RAMDISK_FILES)
+ $(call pretty,"Target ram disk: $@")
+ $(hide) $(MKBOOTFS) $(TARGET_ROOT_OUT) | gzip > $@
+
+
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
+
+# -----------------------------------------------------------------
+# the boot image, which is a collection of other images.
+INTERNAL_BOOTIMAGE_ARGS := \
+ $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
+ --kernel $(INSTALLED_KERNEL_TARGET) \
+ --ramdisk $(INSTALLED_RAMDISK_TARGET)
+
+INTERNAL_BOOTIMAGE_FILES := $(filter-out --%,$(INTERNAL_BOOTIMAGE_ARGS))
+
+BOARD_KERNEL_CMDLINE := $(strip $(BOARD_KERNEL_CMDLINE))
+ifdef BOARD_KERNEL_CMDLINE
+ INTERNAL_BOOTIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
+endif
+
+INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
+
+ifeq ($(TARGET_BOOTIMAGE_USE_EXT2),true)
+tmp_dir_for_image := $(call intermediates-dir-for,EXECUTABLES,boot_img)/bootimg
+INTERNAL_BOOTIMAGE_ARGS += --tmpdir $(tmp_dir_for_image)
+INTERNAL_BOOTIMAGE_ARGS += --genext2fs $(MKEXT2IMG)
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKEXT2IMG) $(INTERNAL_BOOTIMAGE_FILES)
+ $(call pretty,"Target boot image: $@")
+ $(hide) $(MKEXT2BOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
+
+else # TARGET_BOOTIMAGE_USE_EXT2 != true
+
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_FILES)
+ $(call pretty,"Target boot image: $@")
+ $(hide) $(MKBOOTIMG) $(INTERNAL_BOOTIMAGE_ARGS) --output $@
+ $(hide) $(call assert-max-file-size,$@,$(BOARD_BOOTIMAGE_MAX_SIZE))
+endif # TARGET_BOOTIMAGE_USE_EXT2
+
+else # TARGET_NO_KERNEL
+# HACK: The top-level targets depend on the bootimage. Not all targets
+# can produce a bootimage, though, and emulator targets need the ramdisk
+# instead. Fake it out by calling the ramdisk the bootimage.
+# TODO: make the emulator use bootimages, and make mkbootimg accept
+# kernel-less inputs.
+INSTALLED_BOOTIMAGE_TARGET := $(INSTALLED_RAMDISK_TARGET)
+endif
+
+# -----------------------------------------------------------------
+# NOTICE files
+#
+# This needs to be before the systemimage rules, because it adds to
+# ALL_DEFAULT_INSTALLED_MODULES, which those use to pick which files
+# go into the systemimage.
+
+.PHONY: notice_files
+
+# Create the rule to combine the files into text and html forms
+# $(1) - Plain text output file
+# $(2) - HTML output file
+# $(3) - File title
+# $(4) - Directory to use. Notice files are all $(4)/src. Other
+# directories in there will be used for scratch
+# $(5) - Dependencies for the output files
+#
+# The algorithm here is that we go collect a hash for each of the notice
+# files and write the names of the files that match that hash. Then
+# to generate the real files, we go print out all of the files and their
+# hashes.
+#
+# These rules are fairly complex, so they depend on this makefile so if
+# it changes, they'll run again.
+#
+# TODO: We could clean this up so that we just record the locations of the
+# original notice files instead of making rules to copy them somwehere.
+# Then we could traverse that without quite as much bash drama.
+define combine-notice-files
+$(1) $(2): PRIVATE_MESSAGE := $(3)
+$(1) $(2) $(4)/hash-timestamp: PRIVATE_DIR := $(4)
+$(4)/hash-timestamp: $(5) $(BUILD_SYSTEM)/Makefile
+ @echo Finding NOTICE files: $$@
+ $$(hide) rm -rf $$@ $$(PRIVATE_DIR)/hash
+ $$(hide) mkdir -p $$(PRIVATE_DIR)/hash
+ $$(hide) for file in $$$$(find $$(PRIVATE_DIR)/src -type f); do \
+ hash=$$$$($(MD5SUM) $$$$file | sed -e "s/ .*//"); \
+ hashfile=$$(PRIVATE_DIR)/hash/$$$$hash; \
+ echo $$$$file >> $$$$hashfile; \
+ done
+ $$(hide) touch $$@
+$(1): $(4)/hash-timestamp
+ @echo Combining NOTICE files: $$@
+ $$(hide) mkdir -p $$(dir $$@)
+ $$(hide) echo $$(PRIVATE_MESSAGE) > $$@
+ $$(hide) find $$(PRIVATE_DIR)/hash -type f | xargs cat | sort | \
+ sed -e "s:$$(PRIVATE_DIR)/src\(.*\)\.txt: \1:" >> $$@
+ $$(hide) echo >> $$@
+ $$(hide) echo >> $$@
+ $$(hide) echo >> $$@
+ $$(hide) for hashfile in $$$$(find $$(PRIVATE_DIR)/hash -type f); do \
+ echo "============================================================"\
+ >> $$@; \
+ echo "Notices for file(s):" >> $$@; \
+ cat $$$$hashfile | sort | \
+ sed -e "s:$$(PRIVATE_DIR)/src\(.*\)\.txt: \1:" >> \
+ $$@; \
+ echo "------------------------------------------------------------"\
+ >> $$@; \
+ echo >> $$@; \
+ orig=$$$$(head -n 1 $$$$hashfile); \
+ cat $$$$orig >> $$@; \
+ echo >> $$@; \
+ echo >> $$@; \
+ echo >> $$@; \
+ done
+$(2): $(4)/hash-timestamp
+ @echo Combining NOTICE files: $$@
+ $$(hide) mkdir -p $$(dir $$@)
+ $$(hide) echo "<html><head>" > $$@
+ $$(hide) echo "<style type=\"text/css\">" >> $$@
+ $$(hide) echo "body { padding: 0; font-family: sans-serif; }" >> $$@
+ $$(hide) echo ".same-license { background-color: #eeeeee; border-top: 20px solid white; padding: 10px; }" >> $$@
+ $$(hide) echo ".label { font-weight: bold; }" >> $$@
+ $$(hide) echo ".file-list { margin-left: 1em; font-color: blue; }" >> $$@
+ $$(hide) echo "</style>" >> $$@
+ $$(hide) echo "</head><body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">" >> $$@
+ $$(hide) echo "<table cellpading=\"0\" cellspacing=\"0\" border=\"0\">" \
+ >> $$@
+ $$(hide) for hashfile in $$$$(find $$(PRIVATE_DIR)/hash -type f); do \
+ cat $$$$hashfile | sort | \
+ sed -e "s:$$(PRIVATE_DIR)/src\(.*\)\.txt: <a name=\"\1\"></a>:" >> \
+ $$@; \
+ echo "<tr><td class=\"same-license\">" >> $$@; \
+ echo "<div class=\"label\">Notices for file(s):</div>" >> $$@; \
+ echo "<div class=\"file-list\">" >> $$@; \
+ cat $$$$hashfile | sort | \
+ sed -e "s:$$(PRIVATE_DIR)/src\(.*\)\.txt: \1<br/>:" >> $$@; \
+ echo "</div><!-- file-list -->" >> $$@; \
+ echo >> $$@; \
+ orig=$$$$(head -n 1 $$$$hashfile); \
+ echo "<pre class=\"license-text\">" >> $$@; \
+ cat $$$$orig | sed -e "s/\&/\&amp;/g" | sed -e "s/</\&lt;/g" \
+ | sed -e "s/>/\&gt;/g" >> $$@; \
+ echo "</pre><!-- license-text -->" >> $$@; \
+ echo "</td></tr><!-- same-license -->" >> $$@; \
+ echo >> $$@; \
+ echo >> $$@; \
+ echo >> $$@; \
+ done
+ $$(hide) echo "</table>" >> $$@
+ $$(hide) echo "</body></html>" >> $$@
+notice_files: $(1) $(2)
+endef
+
+# TODO These intermediate NOTICE.txt/NOTICE.html files should go into
+# TARGET_OUT_NOTICE_FILES now that the notice files are gathered from
+# the src subdirectory.
+
+target_notice_file_txt := $(TARGET_OUT_INTERMEDIATES)/NOTICE.txt
+target_notice_file_html := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html
+target_notice_file_html_gz := $(TARGET_OUT_INTERMEDIATES)/NOTICE.html.gz
+tools_notice_file_txt := $(HOST_OUT_INTERMEDIATES)/NOTICE.txt
+tools_notice_file_html := $(HOST_OUT_INTERMEDIATES)/NOTICE.html
+
+kernel_notice_file := $(TARGET_OUT_NOTICE_FILES)/src/kernel.txt
+
+$(eval $(call combine-notice-files, \
+ $(target_notice_file_txt), \
+ $(target_notice_file_html), \
+ "Notices for files contained in the filesystem images in this directory:", \
+ $(TARGET_OUT_NOTICE_FILES), \
+ $(ALL_DEFAULT_INSTALLED_MODULES) $(kernel_notice_file)))
+
+$(eval $(call combine-notice-files, \
+ $(tools_notice_file_txt), \
+ $(tools_notice_file_html), \
+ "Notices for files contained in the tools directory:", \
+ $(HOST_OUT_NOTICE_FILES), \
+ $(ALL_DEFAULT_INSTALLED_MODULES)))
+
+# Install the html file at /system/etc/NOTICE.html.gz.
+# This is not ideal, but this is very late in the game, after a lot of
+# the module processing has already been done -- in fact, we used the
+# fact that all that has been done to get the list of modules that we
+# need notice files for.
+$(target_notice_file_html_gz): $(target_notice_file_html)
+ gzip -c $< > $@
+installed_notice_html_gz := $(TARGET_OUT)/etc/NOTICE.html.gz
+$(installed_notice_html_gz): $(target_notice_file_html_gz) | $(ACP)
+ $(copy-file-to-target)
+ALL_DEFAULT_INSTALLED_MODULES += $(installed_notice_html_gz)
+
+# The kernel isn't really a module, so to get its module file in there, we
+# make the target NOTICE files depend on this particular file too, which will
+# then be in the right directory for the find in combine-notice-files to work.
+$(kernel_notice_file): \
+ prebuilt/$(TARGET_PREBUILT_TAG)/kernel/LINUX_KERNEL_COPYING \
+ | $(ACP)
+ @echo Copying: $@
+ $(hide) mkdir -p $(dir $@)
+ $(hide) $(ACP) $< $@
+
+
+# #################################################################
+# Targets for user images
+# #################################################################
+
+ifeq ($(TARGET_USERIMAGES_USE_EXT2),true)
+include external/genext2fs/Config.mk
+INTERNAL_MKUSERFS := $(MKEXT2IMG)
+else
+INTERNAL_MKUSERFS := $(MKYAFFS2)
+endif
+
+# -----------------------------------------------------------------
+# system yaffs image
+#
+# First, the "unoptimized" image, which contains .apk/.jar files
+# that contain regular, unoptimized/unverified .dex entries.
+#
+systemimage_unopt_intermediates := \
+ $(call intermediates-dir-for,PACKAGING,systemimage_unopt)
+BUILT_SYSTEMIMAGE_UNOPT := $(systemimage_unopt_intermediates)/system.img
+
+INTERNAL_SYSTEMIMAGE_FILES := $(filter $(TARGET_OUT)/%, \
+ $(ALL_PREBUILT) \
+ $(ALL_COPIED_HEADERS) \
+ $(ALL_GENERATED_SOURCES) \
+ $(ALL_DEFAULT_INSTALLED_MODULES))
+
+ifeq ($(TARGET_USERIMAGES_USE_EXT2),true)
+## generate an ext2 image
+# $(1): output file
+define build-systemimage-target
+ @echo "Target system fs image: $(1)"
+ $(call build-userimage-ext2-target,$(TARGET_OUT),$(1),system,)
+endef
+
+else # TARGET_USERIMAGES_USE_EXT2 != true
+
+## generate a yaffs2 image
+# $(1): output file
+define build-systemimage-target
+ @echo "Target system fs image: $(1)"
+ @mkdir -p $(dir $(1))
+ $(hide) $(MKYAFFS2) -f $(TARGET_OUT) $(1)
+endef
+endif # TARGET_USERIMAGES_USE_EXT2
+
+$(BUILT_SYSTEMIMAGE_UNOPT): $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_MKUSERFS)
+ $(call build-systemimage-target,$@)
+
+# The installed image, which may be optimized or unoptimized.
+#
+INSTALLED_SYSTEMIMAGE := $(PRODUCT_OUT)/system.img
+
+ifdef WITH_DEXPREOPT
+ ifndef DISABLE_DEXPREOPT
+ with_dexpreopt := true
+ endif
+endif
+ifdef with_dexpreopt
+ # This file will set BUILT_SYSTEMIMAGE and SYSTEMIMAGE_SOURCE_DIR
+ include build/tools/dexpreopt/Config.mk
+else
+ BUILT_SYSTEMIMAGE := $(BUILT_SYSTEMIMAGE_UNOPT)
+ SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)
+endif
+
+$(INSTALLED_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE) | $(ACP)
+ @echo "Install system fs image: $@"
+ $(copy-file-to-target)
+ $(hide) $(call assert-max-file-size,$@,$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+
+systemimage: $(INSTALLED_SYSTEMIMAGE)
+
+.PHONY: systemimage-nodeps snod
+systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \
+ | $(INTERNAL_MKUSERFS)
+ @echo "make $@: ignoring dependencies"
+ $(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE))
+ $(hide) $(call assert-max-file-size,$(INSTALLED_SYSTEMIMAGE),$(BOARD_SYSTEMIMAGE_MAX_SIZE))
+
+#######
+## system tarball
+define build-systemtarball-target
+ $(call pretty,"Target system fs tarball: $(INSTALLED_SYSTEMTARBALL_TARGET)")
+ $(MKTARBALL) $(FS_GET_STATS) \
+ $(PRODUCT_OUT) system $(PRIVATE_SYSTEM_TAR) \
+ $(INSTALLED_SYSTEMTARBALL_TARGET)
+endef
+
+system_tar := $(PRODUCT_OUT)/system.tar
+INSTALLED_SYSTEMTARBALL_TARGET := $(system_tar).bz2
+$(INSTALLED_SYSTEMTARBALL_TARGET): PRIVATE_SYSTEM_TAR := $(system_tar)
+$(INSTALLED_SYSTEMTARBALL_TARGET): $(FS_GET_STATS) $(INTERNAL_SYSTEMIMAGE_FILES)
+ $(build-systemtarball-target)
+
+.PHONY: systemtarball-nodeps
+systemtarball-nodeps: $(FS_GET_STATS) \
+ $(filter-out systemtarball-nodeps stnod,$(MAKECMDGOALS))
+ $(build-systemtarball-target)
+
+.PHONY: stnod
+stnod: systemtarball-nodeps
+
+
+# -----------------------------------------------------------------
+# data partition image
+INTERNAL_USERDATAIMAGE_FILES := \
+ $(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
+
+ifeq ($(TARGET_USERIMAGES_USE_EXT2),true)
+## Generate an ext2 image
+define build-userdataimage-target
+ $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
+ @mkdir -p $(TARGET_OUT_DATA)
+ $(call build-userimage-ext2-target,$(TARGET_OUT_DATA),$(INSTALLED_USERDATAIMAGE_TARGET),userdata,)
+ $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE))
+endef
+
+else # TARGET_USERIMAGES_USE_EXT2 != true
+
+## Generate a yaffs2 image
+define build-userdataimage-target
+ $(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")
+ @mkdir -p $(TARGET_OUT_DATA)
+ $(hide) $(MKYAFFS2) -f $(TARGET_OUT_DATA) $(INSTALLED_USERDATAIMAGE_TARGET)
+ $(hide) $(call assert-max-file-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_MAX_SIZE))
+endef
+endif # TARGET_USERIMAGES_USE_EXT2
+
+INSTALLED_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
+$(INSTALLED_USERDATAIMAGE_TARGET): $(INTERNAL_MKUSERFS) \
+ $(INTERNAL_USERDATAIMAGE_FILES)
+ $(build-userdataimage-target)
+
+.PHONY: userdataimage-nodeps
+userdataimage-nodeps: $(INTERNAL_MKUSERFS)
+ $(build-userdataimage-target)
+
+#######
+## data partition tarball
+define build-userdatatarball-target
+ $(call pretty,"Target userdata fs tarball: " \
+ "$(INSTALLED_USERDATATARBALL_TARGET)")
+ $(MKTARBALL) $(FS_GET_STATS) \
+ $(PRODUCT_OUT) data $(PRIVATE_USERDATA_TAR) \
+ $(INSTALLED_USERDATATARBALL_TARGET)
+endef
+
+userdata_tar := $(PRODUCT_OUT)/userdata.tar
+INSTALLED_USERDATATARBALL_TARGET := $(userdata_tar).bz2
+$(INSTALLED_USERDATATARBALL_TARGET): PRIVATE_USERDATA_TAR := $(userdata_tar)
+$(INSTALLED_USERDATATARBALL_TARGET): $(FS_GET_STATS) $(INTERNAL_USERDATAIMAGE_FILES)
+ $(build-userdatatarball-target)
+
+.PHONY: userdatatarball-nodeps
+userdatatarball-nodeps: $(FS_GET_STATS)
+ $(build-userdatatarball-target)
+
+
+# If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
+ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
+
+# -----------------------------------------------------------------
+# Recovery image
+INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
+
+recovery_initrc := $(call include-path-for, recovery)/etc/init.rc
+recovery_kernel := $(INSTALLED_KERNEL_TARGET) # same as a non-recovery system
+recovery_ramdisk := $(PRODUCT_OUT)/ramdisk-recovery.img
+recovery_build_prop := $(INSTALLED_BUILD_PROP_TARGET)
+recovery_binary := $(call intermediates-dir-for,EXECUTABLES,recovery)/recovery
+recovery_resources_common := $(call include-path-for, recovery)/res
+recovery_resources_private := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery/res))
+recovery_resource_deps := $(shell find $(recovery_resources_common) \
+ $(recovery_resources_private) -type f)
+
+ifeq ($(recovery_resources_private),)
+ $(info No private recovery resources for TARGET_DEVICE $(TARGET_DEVICE))
+endif
+
+INTERNAL_RECOVERYIMAGE_ARGS := \
+ $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET)) \
+ --kernel $(recovery_kernel) \
+ --ramdisk $(recovery_ramdisk)
+
+# Assumes this has already been stripped
+ifdef BOARD_KERNEL_CMDLINE
+ INTERNAL_RECOVERYIMAGE_ARGS += --cmdline "$(BOARD_KERNEL_CMDLINE)"
+endif
+
+$(INSTALLED_RECOVERYIMAGE_TARGET): $(MKBOOTFS) $(MKBOOTIMG) \
+ $(INSTALLED_RAMDISK_TARGET) \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(recovery_binary) \
+ $(recovery_initrc) $(recovery_kernel) \
+ $(INSTALLED_2NDBOOTLOADER_TARGET) \
+ $(recovery_build_prop) $(recovery_resource_deps)
+ @echo ----- Making recovery image ------
+ rm -rf $(TARGET_RECOVERY_OUT)
+ mkdir -p $(TARGET_RECOVERY_OUT)
+ mkdir -p $(TARGET_RECOVERY_ROOT_OUT)
+ mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/etc
+ mkdir -p $(TARGET_RECOVERY_ROOT_OUT)/tmp
+ echo Copying baseline ramdisk...
+ cp -R $(TARGET_ROOT_OUT) $(TARGET_RECOVERY_OUT)
+ echo Modifying ramdisk contents...
+ cp -f $(recovery_initrc) $(TARGET_RECOVERY_ROOT_OUT)/
+ cp -f $(recovery_binary) $(TARGET_RECOVERY_ROOT_OUT)/sbin/
+ cp -rf $(recovery_resources_common) $(TARGET_RECOVERY_ROOT_OUT)/
+ $(foreach item,$(recovery_resources_private), \
+ cp -rf $(item) $(TARGET_RECOVERY_ROOT_OUT)/)
+ cat $(INSTALLED_DEFAULT_PROP_TARGET) $(recovery_build_prop) \
+ > $(TARGET_RECOVERY_ROOT_OUT)/default.prop
+ $(MKBOOTFS) $(TARGET_RECOVERY_ROOT_OUT) | gzip > $(recovery_ramdisk)
+ $(MKBOOTIMG) $(INTERNAL_RECOVERYIMAGE_ARGS) --output $@
+ @echo ----- Made recovery image -------- $@
+ $(hide) $(call assert-max-file-size,$@,$(BOARD_RECOVERYIMAGE_MAX_SIZE))
+
+else
+INSTALLED_RECOVERYIMAGE_TARGET :=
+endif
+
+.PHONY: recoveryimage
+recoveryimage: $(INSTALLED_RECOVERYIMAGE_TARGET)
+
+# -----------------------------------------------------------------
+# bring in the installer image generation defines if necessary
+ifeq ($(TARGET_USE_DISKINSTALLER),true)
+include bootable/diskinstaller/config.mk
+endif
+
+# -----------------------------------------------------------------
+# OTA update package
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-ota-$(FILE_NAME_TAG)
+
+INTERNAL_OTA_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+INTERNAL_OTA_INTERMEDIATES_DIR := $(call intermediates-dir-for,PACKAGING,ota)
+
+# If neither TARGET_NO_KERNEL nor TARGET_NO_RECOVERY are true
+ifeq (,$(filter true, $(TARGET_NO_KERNEL) $(TARGET_NO_RECOVERY)))
+INTERNAL_OTA_RECOVERYIMAGE_TARGET := $(INTERNAL_OTA_INTERMEDIATES_DIR)/system/recovery.img
+else
+INTERNAL_OTA_RECOVERYIMAGE_TARGET :=
+endif
+INTERNAL_OTA_SCRIPT_TARGET := $(INTERNAL_OTA_INTERMEDIATES_DIR)/META-INF/com/google/android/update-script
+
+# Sign OTA packages with the test key by default.
+# Actual product deliverables will be re-signed by hand.
+private_key := $(SRC_TARGET_DIR)/product/security/testkey.pk8
+certificate := $(SRC_TARGET_DIR)/product/security/testkey.x509.pem
+$(INTERNAL_OTA_PACKAGE_TARGET): $(private_key) $(certificate) $(SIGNAPK_JAR)
+$(INTERNAL_OTA_PACKAGE_TARGET): PRIVATE_PRIVATE_KEY := $(private_key)
+$(INTERNAL_OTA_PACKAGE_TARGET): PRIVATE_CERTIFICATE := $(certificate)
+
+# Depending on INSTALLED_SYSTEMIMAGE guarantees that SYSTEMIMAGE_SOURCE_DIR
+# is up-to-date. We use jar instead of zip so that we can use the -C
+# switch to avoid cd-ing all over the place.
+# TODO: Make our own jar-creation tool to avoid all these shenanigans.
+$(INTERNAL_OTA_PACKAGE_TARGET): \
+ $(INTERNAL_OTA_SCRIPT_TARGET) \
+ $(INTERNAL_OTA_RECOVERYIMAGE_TARGET) \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_RADIOIMAGE_TARGET) \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
+ $(INSTALLED_SYSTEMIMAGE)
+ @echo "Package OTA: $@"
+ $(hide) rm -rf $@
+ $(hide) jar cf $@ \
+ $(foreach item, \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_RADIOIMAGE_TARGET) \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET), \
+ -C $(dir $(item)) $(notdir $(item))) \
+ -C $(INTERNAL_OTA_INTERMEDIATES_DIR) .
+ $(hide) find $(SYSTEMIMAGE_SOURCE_DIR) -type f -print | \
+ sed 's|^$(dir $(SYSTEMIMAGE_SOURCE_DIR))|-C & |' | \
+ xargs jar uf $@
+ $(hide) if jar tf $@ | egrep '.{65}' >&2; then \
+ echo "Path too long (>64 chars) for OTA update" >&2; \
+ exit 1; \
+ fi
+ $(sign-package)
+
+$(INTERNAL_OTA_SCRIPT_TARGET): \
+ $(HOST_OUT_EXECUTABLES)/make-update-script \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
+ $(INSTALLED_SYSTEMIMAGE)
+ @mkdir -p $(dir $@)
+ @rm -rf $@
+ @echo "Update script: $@"
+ $(hide) TARGET_DEVICE=$(TARGET_DEVICE) \
+ $< $(SYSTEMIMAGE_SOURCE_DIR) \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
+ > $@
+
+ifneq (,$(INTERNAL_OTA_RECOVERYIMAGE_TARGET))
+# This copy is so recovery.img can be in /system within the OTA package.
+# That way it gets installed into the system image, which in turn installs it.
+$(INTERNAL_OTA_RECOVERYIMAGE_TARGET): $(INSTALLED_RECOVERYIMAGE_TARGET) | $(ACP)
+ @mkdir -p $(dir $@)
+ $(hide) $(ACP) $< $@
+endif
+
+.PHONY: otapackage
+otapackage: $(INTERNAL_OTA_PACKAGE_TARGET)
+
+# Keys authorized to sign OTA packages this build will accept.
+ifeq ($(INCLUDE_TEST_OTA_KEYS),true)
+ OTA_PUBLIC_KEYS := \
+ $(sort $(SRC_TARGET_DIR)/product/security/testkey.x509.pem $(OTA_PUBLIC_KEYS))
+endif
+
+ifeq ($(OTA_PUBLIC_KEYS),)
+ $(error No OTA_PUBLIC_KEYS defined)
+endif
+
+# Build a keystore with the authorized keys in it.
+# java/android/android/server/checkin/UpdateVerifier.java uses this.
+ALL_DEFAULT_INSTALLED_MODULES += $(TARGET_OUT_ETC)/security/otacerts.zip
+$(TARGET_OUT_ETC)/security/otacerts.zip: $(OTA_PUBLIC_KEYS)
+ $(hide) rm -f $@
+ $(hide) mkdir -p $(dir $@)
+ zip -qj $@ $(OTA_PUBLIC_KEYS)
+
+# The device does not support JKS.
+# $(hide) for f in $(OTA_PUBLIC_KEYS); do \
+# echo "keytool: $@ <= $$f" && \
+# keytool -keystore $@ -storepass $(notdir $@) -noprompt \
+# -import -file $$f -alias $(notdir $$f) || exit 1; \
+# done
+
+ifdef RECOVERY_INSTALL_OTA_KEYS_INC
+# Generate a C-includable file containing the keys.
+# RECOVERY_INSTALL_OTA_KEYS_INC is defined by recovery/Android.mk.
+# *** THIS IS A TOTAL HACK; EXECUTABLES MUST NOT CHANGE BETWEEN DIFFERENT
+# PRODUCTS/BUILD TYPES. ***
+# TODO: make recovery read the keys from an external file.
+DUMPKEY_JAR := $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar
+$(RECOVERY_INSTALL_OTA_KEYS_INC): PRIVATE_OTA_PUBLIC_KEYS := $(OTA_PUBLIC_KEYS)
+$(RECOVERY_INSTALL_OTA_KEYS_INC): $(OTA_PUBLIC_KEYS) $(DUMPKEY_JAR)
+ @echo "DumpPublicKey: $@ <= $(PRIVATE_OTA_PUBLIC_KEYS)"
+ @rm -rf $@
+ @mkdir -p $(dir $@)
+ $(hide) java -jar $(DUMPKEY_JAR) $(PRIVATE_OTA_PUBLIC_KEYS) > $@
+endif
+
+# -----------------------------------------------------------------
+# A zip of the directories that map to the target filesystem.
+# This zip can be used to create an OTA package or filesystem image
+# as a post-build step.
+#
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-target_files-$(FILE_NAME_TAG)
+
+intermediates := $(call intermediates-dir-for,PACKAGING,target_files)
+BUILT_TARGET_FILES_PACKAGE := $(intermediates)/$(name).zip
+$(BUILT_TARGET_FILES_PACKAGE): intermediates := $(intermediates)
+$(BUILT_TARGET_FILES_PACKAGE): \
+ zip_root := $(intermediates)/$(name)
+
+# $(1): Directory to copy
+# $(2): Location to copy it to
+# The "ls -A" is to prevent "acp s/* d" from failing if s is empty.
+define package_files-copy-root
+ if [ -d "$(strip $(1))" -a "$$(ls -A $(1))" ]; then \
+ mkdir -p $(2) && \
+ $(ACP) -rd $(strip $(1))/* $(2); \
+ fi
+endef
+
+built_ota_tools := \
+ $(call intermediates-dir-for,EXECUTABLES,applypatch)/applypatch \
+ $(call intermediates-dir-for,EXECUTABLES,check_prereq)/check_prereq
+$(BUILT_TARGET_FILES_PACKAGE): PRIVATE_OTA_TOOLS := $(built_ota_tools)
+
+# Depending on the various images guarantees that the underlying
+# directories are up-to-date.
+$(BUILT_TARGET_FILES_PACKAGE): \
+ $(INTERNAL_OTA_SCRIPT_TARGET) \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_RADIOIMAGE_TARGET) \
+ $(INSTALLED_RECOVERYIMAGE_TARGET) \
+ $(BUILT_SYSTEMIMAGE) \
+ $(INSTALLED_USERDATAIMAGE_TARGET) \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET) \
+ $(INTERNAL_OTA_SCRIPT_TARGET) \
+ $(built_ota_tools) \
+ $(APKCERTS_FILE) \
+ | $(ACP)
+ @echo "Package target files: $@"
+ $(hide) rm -rf $@ $(zip_root)
+ $(hide) mkdir -p $(dir $@) $(zip_root)
+ @# Components of the recovery image
+ $(hide) mkdir -p $(zip_root)/RECOVERY
+ $(hide) $(call package_files-copy-root, \
+ $(TARGET_RECOVERY_ROOT_OUT),$(zip_root)/RECOVERY/RAMDISK)
+ifdef INSTALLED_KERNEL_TARGET
+ $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/RECOVERY/kernel
+endif
+ifdef INSTALLED_2NDBOOTLOADER_TARGET
+ $(hide) $(ACP) \
+ $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/RECOVERY/second
+endif
+ifdef BOARD_KERNEL_CMDLINE
+ $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/RECOVERY/cmdline
+endif
+ @# Components of the boot image
+ $(hide) mkdir -p $(zip_root)/BOOT
+ $(hide) $(call package_files-copy-root, \
+ $(TARGET_ROOT_OUT),$(zip_root)/BOOT/RAMDISK)
+ifdef INSTALLED_KERNEL_TARGET
+ $(hide) $(ACP) $(INSTALLED_KERNEL_TARGET) $(zip_root)/BOOT/kernel
+endif
+ifdef INSTALLED_2NDBOOTLOADER_TARGET
+ $(hide) $(ACP) \
+ $(INSTALLED_2NDBOOTLOADER_TARGET) $(zip_root)/BOOT/second
+endif
+ifdef BOARD_KERNEL_CMDLINE
+ $(hide) echo "$(BOARD_KERNEL_CMDLINE)" > $(zip_root)/BOOT/cmdline
+endif
+ifdef INSTALLED_RADIOIMAGE_TARGET
+ @# The radio image
+ $(hide) mkdir -p $(zip_root)/RADIO
+ $(hide) $(ACP) $(INSTALLED_RADIOIMAGE_TARGET) $(zip_root)/RADIO/image
+endif
+ @# Contents of the system image
+ $(hide) $(call package_files-copy-root, \
+ $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
+ @# Contents of the data image
+ $(hide) $(call package_files-copy-root, \
+ $(TARGET_OUT_DATA),$(zip_root)/DATA)
+ @# Extra contents of the OTA package
+ $(hide) mkdir -p $(zip_root)/OTA/bin
+ $(hide) $(call package_files-copy-root, \
+ $(INTERNAL_OTA_INTERMEDIATES_DIR),$(zip_root)/OTA)
+ $(hide) $(ACP) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(zip_root)/OTA/
+ $(hide) $(ACP) $(PRIVATE_OTA_TOOLS) $(zip_root)/OTA/bin/
+ @# Files that don't end up in any images, but are necessary to
+ @# build them.
+ $(hide) mkdir -p $(zip_root)/META
+ $(hide) $(ACP) $(APKCERTS_FILE) $(zip_root)/META/apkcerts.txt
+ @# Zip everything up, preserving symlinks
+ $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
+
+target-files-package: $(BUILT_TARGET_FILES_PACKAGE)
+
+# -----------------------------------------------------------------
+# installed file list
+# Depending on $(INSTALLED_SYSTEMIMAGE) ensures that it
+# gets the DexOpt one if we're doing that.
+INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt
+$(INSTALLED_FILES_FILE): $(INSTALLED_SYSTEMIMAGE)
+ @echo Installed file list: $@
+ @mkdir -p $(dir $@)
+ @rm -f $@
+ $(hide) build/tools/fileslist.py $(TARGET_OUT) $(TARGET_OUT_DATA) > $@
+
+.PHONY: installed-file-list
+installed-file-list: $(INSTALLED_FILES_FILE)
+$(call dist-for-goals, sdk, $(INSTALLED_FILES_FILE))
+
+# -----------------------------------------------------------------
+# A zip of the tests that are built when running "make tests".
+# This is very similar to BUILT_TARGET_FILES_PACKAGE, but we
+# only grab SYSTEM and DATA, and it's called "*-tests-*.zip".
+#
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-tests-$(FILE_NAME_TAG)
+
+intermediates := $(call intermediates-dir-for,PACKAGING,tests_zip)
+BUILT_TESTS_ZIP_PACKAGE := $(intermediates)/$(name).zip
+$(BUILT_TESTS_ZIP_PACKAGE): intermediates := $(intermediates)
+$(BUILT_TESTS_ZIP_PACKAGE): zip_root := $(intermediates)/$(name)
+
+# Depending on the images guarantees that the underlying
+# directories are up-to-date.
+$(BUILT_TESTS_ZIP_PACKAGE): \
+ $(BUILT_SYSTEMIMAGE) \
+ $(INSTALLED_USERDATAIMAGE_TARGET) \
+ | $(ACP)
+ @echo "Package test files: $@"
+ $(hide) rm -rf $@ $(zip_root)
+ $(hide) mkdir -p $(dir $@) $(zip_root)
+ @# Some parts of the system image
+ $(hide) $(call package_files-copy-root, \
+ $(SYSTEMIMAGE_SOURCE_DIR)/xbin,$(zip_root)/SYSTEM/xbin)
+ $(hide) $(call package_files-copy-root, \
+ $(SYSTEMIMAGE_SOURCE_DIR)/lib,$(zip_root)/SYSTEM/lib)
+ $(hide) $(call package_files-copy-root, \
+ $(SYSTEMIMAGE_SOURCE_DIR)/framework, \
+ $(zip_root)/SYSTEM/framework)
+ $(hide) $(ACP) $(SYSTEMIMAGE_SOURCE_DIR)/build.prop $(zip_root)/SYSTEM
+ @# Contents of the data image
+ $(hide) $(call package_files-copy-root, \
+ $(TARGET_OUT_DATA),$(zip_root)/DATA)
+ $(hide) (cd $(zip_root) && zip -qry ../$(notdir $@) .)
+
+tests-zip-package: $(BUILT_TESTS_ZIP_PACKAGE)
+
+# -----------------------------------------------------------------
+# A zip of the symbols directory. Keep the full paths to make it
+# more obvious where these files came from.
+#
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-symbols-$(FILE_NAME_TAG)
+
+SYMBOLS_ZIP := $(PRODUCT_OUT)/$(name).zip
+$(SYMBOLS_ZIP): $(INSTALLED_SYSTEMIMAGE) $(INSTALLED_BOOTIMAGE_TARGET)
+ @echo "Package symbols: $@"
+ $(hide) rm -rf $@
+ $(hide) mkdir -p $(dir $@)
+ $(hide) zip -qr $@ $(TARGET_OUT_UNSTRIPPED)
+
+# -----------------------------------------------------------------
+# A zip of the Android Apps. Not keeping full path so that we don't
+# include product names when distributing
+#
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-apps-$(FILE_NAME_TAG)
+
+APPS_ZIP := $(PRODUCT_OUT)/$(name).zip
+$(APPS_ZIP): $(INSTALLED_SYSTEMIMAGE)
+ @echo "Package apps: $@"
+ $(hide) rm -rf $@
+ $(hide) mkdir -p $(dir $@)
+ $(hide) zip -qj $@ $(TARGET_OUT_APPS)/*
+
+endif # TARGET_SIMULATOR != true
+
+# -----------------------------------------------------------------
+# dalvik something
+.PHONY: dalvikfiles
+dalvikfiles: $(INTERNAL_DALVIK_MODULES)
+
+# -----------------------------------------------------------------
+# The update package
+
+INTERNAL_UPDATE_PACKAGE_FILES += \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_RECOVERYIMAGE_TARGET) \
+ $(INSTALLED_SYSTEMIMAGE) \
+ $(INSTALLED_USERDATAIMAGE_TARGET) \
+ $(INSTALLED_ANDROID_INFO_TXT_TARGET)
+
+ifneq ($(strip $(INTERNAL_UPDATE_PACKAGE_FILES)),)
+
+name := $(TARGET_PRODUCT)
+ifeq ($(TARGET_BUILD_TYPE),debug)
+ name := $(name)_debug
+endif
+name := $(name)-img-$(FILE_NAME_TAG)
+
+INTERNAL_UPDATE_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+
+$(INTERNAL_UPDATE_PACKAGE_TARGET): $(INTERNAL_UPDATE_PACKAGE_FILES)
+ @echo "Package: $@"
+ $(hide) zip -qj $@ $(INTERNAL_UPDATE_PACKAGE_FILES)
+
+else
+INTERNAL_UPDATE_PACKAGE_TARGET :=
+endif
+
+# -----------------------------------------------------------------
+# The emulator package
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+INTERNAL_EMULATOR_PACKAGE_FILES += \
+ $(HOST_OUT_EXECUTABLES)/emulator$(HOST_EXECUTABLE_SUFFIX) \
+ prebuilt/android-arm/kernel/kernel-qemu \
+ $(INSTALLED_RAMDISK_TARGET) \
+ $(INSTALLED_SYSTEMIMAGE) \
+ $(INSTALLED_USERDATAIMAGE_TARGET)
+
+name := $(TARGET_PRODUCT)-emulator-$(FILE_NAME_TAG)
+
+INTERNAL_EMULATOR_PACKAGE_TARGET := $(PRODUCT_OUT)/$(name).zip
+
+$(INTERNAL_EMULATOR_PACKAGE_TARGET): $(INTERNAL_EMULATOR_PACKAGE_FILES)
+ @echo "Package: $@"
+ $(hide) zip -qj $@ $(INTERNAL_EMULATOR_PACKAGE_FILES)
+
+endif
+
+# -----------------------------------------------------------------
+# The pdk package (Platform Development Kit)
+
+ifneq (,$(filter pdk,$(MAKECMDGOALS)))
+ include development/pdk/Pdk.mk
+endif
+
+# -----------------------------------------------------------------
+# The SDK
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+# The SDK includes host-specific components, so it belongs under HOST_OUT.
+sdk_dir := $(HOST_OUT)/sdk
+
+# Build a name that looks like:
+#
+# linux-x86 --> android-sdk_12345_linux-x86
+# darwin-x86 --> android-sdk_12345_mac-x86
+# windows-x86 --> android-sdk_12345_windows
+#
+sdk_name := android-sdk_$(FILE_NAME_TAG)
+ifeq ($(HOST_OS),darwin)
+ sdk_host_os := mac
+else
+ sdk_host_os := $(HOST_OS)
+endif
+ifneq ($(HOST_OS),windows)
+ sdk_host_os := $(sdk_host_os)-$(HOST_ARCH)
+endif
+sdk_name := $(sdk_name)_$(sdk_host_os)
+
+sdk_dep_file := $(sdk_dir)/sdk_deps.mk
+
+ATREE_FILES :=
+-include $(sdk_dep_file)
+
+# if we don't have a real list, then use "everything"
+ifeq ($(strip $(ATREE_FILES)),)
+ATREE_FILES := \
+ $(ALL_PREBUILT) \
+ $(ALL_COPIED_HEADERS) \
+ $(ALL_GENERATED_SOURCES) \
+ $(ALL_DEFAULT_INSTALLED_MODULES) \
+ $(INSTALLED_RAMDISK_TARGET) \
+ $(ALL_DOCS) \
+ $(ALL_SDK_FILES)
+endif
+
+atree_dir := development/build
+
+sdk_atree_files := \
+ $(atree_dir)/sdk.exclude.atree \
+ $(atree_dir)/sdk.atree \
+ $(atree_dir)/sdk-$(HOST_OS)-$(HOST_ARCH).atree
+
+deps := \
+ $(target_notice_file_txt) \
+ $(tools_notice_file_txt) \
+ $(OUT_DOCS)/offline-sdk-timestamp \
+ $(INTERNAL_UPDATE_PACKAGE_TARGET) \
+ $(INSTALLED_SDK_BUILD_PROP_TARGET) \
+ $(ATREE_FILES) \
+ $(atree_dir)/sdk.atree \
+ $(HOST_OUT_EXECUTABLES)/atree \
+ $(HOST_OUT_EXECUTABLES)/line_endings
+
+INTERNAL_SDK_TARGET := $(sdk_dir)/$(sdk_name).zip
+$(INTERNAL_SDK_TARGET): PRIVATE_NAME := $(sdk_name)
+$(INTERNAL_SDK_TARGET): PRIVATE_DIR := $(sdk_dir)/$(sdk_name)
+$(INTERNAL_SDK_TARGET): PRIVATE_DEP_FILE := $(sdk_dep_file)
+$(INTERNAL_SDK_TARGET): PRIVATE_INPUT_FILES := $(sdk_atree_files)
+
+# Set SDK_GNU_ERROR to non-empty to fail when a GNU target is built.
+#
+#SDK_GNU_ERROR := true
+
+$(INTERNAL_SDK_TARGET): $(deps)
+ @echo "Package SDK: $@"
+ $(hide) rm -rf $(PRIVATE_DIR) $@
+ $(hide) for f in $(target_gnu_MODULES); do \
+ if [ -f $$f ]; then \
+ echo SDK: $(if $(SDK_GNU_ERROR),ERROR:,warning:) \
+ including GNU target $$f >&2; \
+ FAIL=$(SDK_GNU_ERROR); \
+ fi; \
+ done; \
+ if [ $$FAIL ]; then exit 1; fi
+ $(hide) ( \
+ $(HOST_OUT_EXECUTABLES)/atree \
+ $(addprefix -f ,$(PRIVATE_INPUT_FILES)) \
+ -m $(PRIVATE_DEP_FILE) \
+ -I . \
+ -I $(PRODUCT_OUT) \
+ -I $(HOST_OUT) \
+ -I $(TARGET_COMMON_OUT_ROOT) \
+ -v "PLATFORM_NAME=android-$(PLATFORM_VERSION)" \
+ -o $(PRIVATE_DIR) && \
+ cp -f $(target_notice_file_txt) \
+ $(PRIVATE_DIR)/platforms/android-$(PLATFORM_VERSION)/images/NOTICE.txt && \
+ cp -f $(tools_notice_file_txt) $(PRIVATE_DIR)/tools/NOTICE.txt && \
+ HOST_OUT_EXECUTABLES=$(HOST_OUT_EXECUTABLES) HOST_OS=$(HOST_OS) \
+ development/tools/scripts/sdk_clean.sh $(PRIVATE_DIR) && \
+ chmod -R ug+rwX $(PRIVATE_DIR) && \
+ cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME) \
+ ) || ( rm -rf $(PRIVATE_DIR) $@ && exit 44 )
+
+endif # !simulator
+
+# -----------------------------------------------------------------
+# Findbugs
+INTERNAL_FINDBUGS_XML_TARGET := $(PRODUCT_OUT)/findbugs.xml
+INTERNAL_FINDBUGS_HTML_TARGET := $(PRODUCT_OUT)/findbugs.html
+$(INTERNAL_FINDBUGS_XML_TARGET): $(ALL_FINDBUGS_FILES)
+ @echo UnionBugs: $@
+ $(hide) prebuilt/common/findbugs/bin/unionBugs $(ALL_FINDBUGS_FILES) \
+ > $@
+$(INTERNAL_FINDBUGS_HTML_TARGET): $(INTERNAL_FINDBUGS_XML_TARGET)
+ @echo ConvertXmlToText: $@
+ $(hide) prebuilt/common/findbugs/bin/convertXmlToText -html:fancy.xsl \
+ $(INTERNAL_FINDBUGS_XML_TARGET) > $@
+
+# -----------------------------------------------------------------
+# Findbugs
+
+# -----------------------------------------------------------------
+# These are some additional build tasks that need to be run.
+include $(sort $(wildcard $(BUILD_SYSTEM)/tasks/*.mk))
diff --git a/core/apicheck_msg_current.txt b/core/apicheck_msg_current.txt
new file mode 100644
index 000000000..c277ecdf9
--- /dev/null
+++ b/core/apicheck_msg_current.txt
@@ -0,0 +1,18 @@
+
+******************************
+You have tried to change the API from what has been previously approved.
+
+To make these errors go away, you have two choices:
+ 1) You can add "@hide" javadoc comments to the methods, etc. listed in the
+ errors above.
+
+ 2) You can update current.xml by executing the following commands:
+
+ p4 edit frameworks/base/api/current.xml
+ make update-api
+
+ To check in the revised current.xml, you will need OWNERS approval.
+******************************
+
+
+
diff --git a/core/apicheck_msg_last.txt b/core/apicheck_msg_last.txt
new file mode 100644
index 000000000..2993157b1
--- /dev/null
+++ b/core/apicheck_msg_last.txt
@@ -0,0 +1,7 @@
+
+******************************
+You have tried to change the API from what has been previously released in
+an SDK. Please fix the errors listed above.
+******************************
+
+
diff --git a/core/armelf.x b/core/armelf.x
new file mode 100644
index 000000000..766fe8862
--- /dev/null
+++ b/core/armelf.x
@@ -0,0 +1,198 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
+ "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SEARCH_DIR("/usr/local/armdev/arm-elf/lib");
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+/* PROVIDE (__executable_start = 0x8000); . = 0x8000; */
+. = 0x8000 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.init : { *(.rel.init) }
+ .rela.init : { *(.rela.init) }
+ .rel.text : { *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*) }
+ .rela.text : { *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*) }
+ .rel.fini : { *(.rel.fini) }
+ .rela.fini : { *(.rela.fini) }
+ .rel.rodata : { *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*) }
+ .rela.rodata : { *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*) }
+ .rel.data.rel.ro : { *(.rel.data.rel.ro*) }
+ .rela.data.rel.ro : { *(.rel.data.rel.ro*) }
+ .rel.data : { *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*) }
+ .rela.data : { *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*) }
+ .rel.tdata : { *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*) }
+ .rela.tdata : { *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*) }
+ .rel.tbss : { *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*) }
+ .rela.tbss : { *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*) }
+ .rel.ctors : { *(.rel.ctors) }
+ .rela.ctors : { *(.rela.ctors) }
+ .rel.dtors : { *(.rel.dtors) }
+ .rela.dtors : { *(.rela.dtors) }
+ .rel.got : { *(.rel.got) }
+ .rela.got : { *(.rela.got) }
+ .rel.bss : { *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*) }
+ .rela.bss : { *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*) }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7)
+ } =0
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ /* We have to wrap extab and exidx sections with KEEP because we use
+ --gc-sections. */
+ .ARM.extab : { KEEP (*(.ARM.extab* .gnu.linkonce.armextab.*)) }
+ __exidx_start = .;
+ .ARM.exidx : { KEEP (*(.ARM.exidx* .gnu.linkonce.armexidx.*)) }
+ __exidx_end = .;
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to align at exactly
+ a page boundary to make life easier for apriori. */
+ . = ALIGN(4096);
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { KEEP (*(.preinit_array)) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { KEEP (*(.init_array)) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { KEEP (*(.fini_array)) }
+ PROVIDE (__fini_array_end = .);
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
+ .dynamic : { *(.dynamic) }
+ .got : { *(.got.plt) *(.got) }
+ .data :
+ {
+ __data_start = . ;
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ _edata = .;
+ PROVIDE (edata = .);
+ __bss_start = .;
+ __bss_start__ = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ _bss_end__ = . ; __bss_end__ = . ; __end__ = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .stack 0x80000 :
+ {
+ _stack = .;
+ *(.stack)
+ }
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/core/armelf.xsc b/core/armelf.xsc
new file mode 100644
index 000000000..425305491
--- /dev/null
+++ b/core/armelf.xsc
@@ -0,0 +1,201 @@
+/* Script for --shared -z combreloc: shared library, combine & sort relocs */
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
+ "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+ . = 0 + SIZEOF_HEADERS;
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+ .gnu.version : { *(.gnu.version) }
+ .gnu.version_d : { *(.gnu.version_d) }
+ .gnu.version_r : { *(.gnu.version_r) }
+ .rel.dyn :
+ {
+ *(.rel.init)
+ *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
+ *(.rel.fini)
+ *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
+ *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
+ *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
+ *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
+ *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
+ *(.rel.ctors)
+ *(.rel.dtors)
+ *(.rel.got)
+ *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
+ }
+ .rela.dyn :
+ {
+ *(.rela.init)
+ *(.rela.text .rela.text.* .rela.gnu.linkonce.t.*)
+ *(.rela.fini)
+ *(.rela.rodata .rela.rodata.* .rela.gnu.linkonce.r.*)
+ *(.rela.data .rela.data.* .rela.gnu.linkonce.d.*)
+ *(.rela.tdata .rela.tdata.* .rela.gnu.linkonce.td.*)
+ *(.rela.tbss .rela.tbss.* .rela.gnu.linkonce.tb.*)
+ *(.rela.ctors)
+ *(.rela.dtors)
+ *(.rela.got)
+ *(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
+ }
+ .rel.plt : { *(.rel.plt) }
+ .rela.plt : { *(.rela.plt) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7)
+ } =0
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ /* We have to wrap extab and exidx sections with KEEP because we use
+ --gc-sections. */
+ .ARM.extab : { KEEP (*(.ARM.extab* .gnu.linkonce.armextab.*)) }
+ __exidx_start = .;
+ .ARM.exidx : { KEEP (*(.ARM.exidx* .gnu.linkonce.armexidx.*)) }
+ __exidx_end = .;
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to align at exactly
+ a page boundary to make life easier for apriori. */
+ . = ALIGN(4096);
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ .preinit_array :
+ {
+ KEEP (*(.preinit_array))
+ }
+ .init_array :
+ {
+ KEEP (*(SORT(.init_array.*)))
+ KEEP (*(.init_array))
+ }
+ .fini_array :
+ {
+ KEEP (*(.fini_array))
+ KEEP (*(SORT(.fini_array.*)))
+ }
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
+ .dynamic : { *(.dynamic) }
+ .got : { *(.got.plt) *(.got) }
+ .data :
+ {
+ __data_start = . ;
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ _edata = .; PROVIDE (edata = .);
+ __bss_start = .;
+ __bss_start__ = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections.
+ FIXME: Why do we need it? When there is no .bss section, we don't
+ pad the .data section. */
+ . = ALIGN(. != 0 ? 32 / 8 : 1);
+ }
+ _bss_end__ = . ; __bss_end__ = . ;
+ . = ALIGN(32 / 8);
+ . = ALIGN(32 / 8);
+ __end__ = . ;
+ _end = .; PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ .comment 0 : { *(.comment) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .stack 0x80000 :
+ {
+ _stack = .;
+ *(.stack)
+ }
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) }
+ /DISCARD/ : { *(.note.GNU-stack) }
+}
diff --git a/core/armelflib.x b/core/armelflib.x
new file mode 100644
index 000000000..0150e02d1
--- /dev/null
+++ b/core/armelflib.x
@@ -0,0 +1,164 @@
+/* Default linker script, for normal executables */
+OUTPUT_FORMAT("elf32-littlearm", "elf32-bigarm",
+ "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SEARCH_DIR("/usr/local/armdev/arm-elf/lib");
+/* Do we need any of these for elf?
+ __DYNAMIC = 0; */
+SECTIONS
+{
+ /* Read-only sections, merged into text segment: */
+/* PROVIDE (__executable_start = 0x8000); . = 0x8000; */
+. = 0 + SIZEOF_HEADERS;
+ .interp : { *(.interp) }
+ .init :
+ {
+ KEEP (*(.init))
+ } =0
+ .plt : { *(.plt) }
+ .text :
+ {
+ *(.text .stub .text.* .gnu.linkonce.t.*)
+ KEEP (*(.text.*personality*))
+ /* .gnu.warning sections are handled specially by elf32.em. */
+ *(.gnu.warning)
+ *(.glue_7t) *(.glue_7)
+ } =0
+ .fini :
+ {
+ KEEP (*(.fini))
+ } =0
+ PROVIDE (__etext = .);
+ PROVIDE (_etext = .);
+ PROVIDE (etext = .);
+ .rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
+ .rodata1 : { *(.rodata1) }
+ .ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) }
+ __exidx_start = .;
+ .ARM.exidx : { *(.ARM.exidx* .gnu.linkonce.armexidx.*) }
+ __exidx_end = .;
+ .eh_frame_hdr : { *(.eh_frame_hdr) }
+ .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RO { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+ /* Adjust the address for the data segment. We want to adjust up to
+ the same address within the page on the next page up. */
+ . = ALIGN(256) + (. & (256 - 1));
+ /* Exception handling */
+ .eh_frame : ONLY_IF_RW { KEEP (*(.eh_frame)) }
+ .gcc_except_table : ONLY_IF_RW { KEEP (*(.gcc_except_table)) *(.gcc_except_table.*) }
+ /* Thread Local Storage sections */
+ .tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
+ .tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
+ /* Ensure the __preinit_array_start label is properly aligned. We
+ could instead move the label definition inside the section, but
+ the linker would then create the section even if it turns out to
+ be empty, which isn't pretty. */
+ . = ALIGN(32 / 8);
+ PROVIDE (__preinit_array_start = .);
+ .preinit_array : { KEEP (*(.preinit_array)) }
+ PROVIDE (__preinit_array_end = .);
+ PROVIDE (__init_array_start = .);
+ .init_array : { KEEP (*(.init_array)) }
+ PROVIDE (__init_array_end = .);
+ PROVIDE (__fini_array_start = .);
+ .fini_array : { KEEP (*(.fini_array)) }
+ PROVIDE (__fini_array_end = .);
+ .ctors :
+ {
+ /* gcc uses crtbegin.o to find the start of
+ the constructors, so we make sure it is
+ first. Because this is a wildcard, it
+ doesn't matter if the user does not
+ actually link against crtbegin.o; the
+ linker won't look for a file to match a
+ wildcard. The wildcard also means that it
+ doesn't matter which directory crtbegin.o
+ is in. */
+ KEEP (*crtbegin*.o(.ctors))
+ /* We don't want to include the .ctor section from
+ from the crtend.o file until after the sorted ctors.
+ The .ctor section from the crtend file contains the
+ end of ctors marker and it must be last */
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .ctors))
+ KEEP (*(SORT(.ctors.*)))
+ KEEP (*(.ctors))
+ }
+ .dtors :
+ {
+ KEEP (*crtbegin*.o(.dtors))
+ KEEP (*(EXCLUDE_FILE (*crtend*.o ) .dtors))
+ KEEP (*(SORT(.dtors.*)))
+ KEEP (*(.dtors))
+ }
+ .jcr : { KEEP (*(.jcr)) }
+ .data.rel.ro : { *(.data.rel.ro.local) *(.data.rel.ro*) }
+ .got : { *(.got.plt) *(.got) }
+ .data :
+ {
+ __data_start = . ;
+ *(.data .data.* .gnu.linkonce.d.*)
+ KEEP (*(.gnu.linkonce.d.*personality*))
+ SORT(CONSTRUCTORS)
+ }
+ .data1 : { *(.data1) }
+ _edata = .;
+ PROVIDE (edata = .);
+ .dynamic : { *(.dynamic) }
+ .hash : { *(.hash) }
+ .dynsym : { *(.dynsym) }
+ .dynstr : { *(.dynstr) }
+/* .shstrtab : { *(.shstrtab) } */
+ .rel.plt : { *(.rel.plt) }
+ .rel.dyn : { *(.rel.*) }
+ __bss_start = .;
+ __bss_start__ = .;
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss .bss.* .gnu.linkonce.b.*)
+ *(COMMON)
+ /* Align here to ensure that the .bss section occupies space up to
+ _end. Align after .bss to ensure correct alignment even if the
+ .bss section disappears because there are no input sections. */
+ . = ALIGN(32 / 8);
+ }
+ . = ALIGN(32 / 8);
+ _end = .;
+ _bss_end__ = . ; __bss_end__ = . ; __end__ = . ;
+ PROVIDE (end = .);
+ /* Stabs debugging sections. */
+ .stab 0 : { *(.stab) }
+ .stabstr 0 : { *(.stabstr) }
+ .stab.excl 0 : { *(.stab.excl) }
+ .stab.exclstr 0 : { *(.stab.exclstr) }
+ .stab.index 0 : { *(.stab.index) }
+ .stab.indexstr 0 : { *(.stab.indexstr) }
+ /* DWARF debug sections.
+ Symbols in the DWARF debugging sections are relative to the beginning
+ of the section so we begin them at 0. */
+ /* DWARF 1 */
+ .debug 0 : { *(.debug) }
+ .line 0 : { *(.line) }
+ /* GNU DWARF 1 extensions */
+ .debug_srcinfo 0 : { *(.debug_srcinfo) }
+ .debug_sfnames 0 : { *(.debug_sfnames) }
+ /* DWARF 1.1 and DWARF 2 */
+ .debug_aranges 0 : { *(.debug_aranges) }
+ .debug_pubnames 0 : { *(.debug_pubnames) }
+ /* DWARF 2 */
+ .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
+ .debug_abbrev 0 : { *(.debug_abbrev) }
+ .debug_line 0 : { *(.debug_line) }
+ .debug_frame 0 : { *(.debug_frame) }
+ .debug_str 0 : { *(.debug_str) }
+ .debug_loc 0 : { *(.debug_loc) }
+ .debug_macinfo 0 : { *(.debug_macinfo) }
+ /* SGI/MIPS DWARF 2 extensions */
+ .debug_weaknames 0 : { *(.debug_weaknames) }
+ .debug_funcnames 0 : { *(.debug_funcnames) }
+ .debug_typenames 0 : { *(.debug_typenames) }
+ .debug_varnames 0 : { *(.debug_varnames) }
+ .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
+ /DISCARD/ : { *(.note.GNU-stack) *(.comment*) *(.stack*) *(.shstrtab) }
+}
diff --git a/core/base_rules.mk b/core/base_rules.mk
new file mode 100644
index 000000000..ba89c406b
--- /dev/null
+++ b/core/base_rules.mk
@@ -0,0 +1,441 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Users can define base-rules-hook in their buildspec.mk to perform
+# arbitrary operations as each module is included.
+ifdef base-rules-hook
+$(if $(base-rules-hook),)
+endif
+
+###########################################################
+## Common instructions for a generic module.
+###########################################################
+
+LOCAL_MODULE := $(strip $(LOCAL_MODULE))
+ifeq ($(LOCAL_MODULE),)
+ $(error $(LOCAL_PATH): LOCAL_MODULE is not defined)
+endif
+
+LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE))
+ifdef LOCAL_IS_HOST_MODULE
+ ifneq ($(LOCAL_IS_HOST_MODULE),true)
+ $(error $(LOCAL_PATH): LOCAL_IS_HOST_MODULE must be "true" or empty, not "$(LOCAL_IS_HOST_MODULE)")
+ endif
+ my_prefix:=HOST_
+ my_host:=host-
+else
+ my_prefix:=TARGET_
+ my_host:=
+endif
+
+###########################################################
+## Validate and define fallbacks for input LOCAL_* variables.
+###########################################################
+
+## Dump a .csv file of all modules and their tags
+#ifneq ($(tag-list-first-time),false)
+#$(shell rm -f tag-list.csv)
+#tag-list-first-time := false
+#endif
+#comma := ,
+#empty :=
+#space := $(empty) $(empty)
+#$(shell echo $(lastword $(filter-out config/% out/%,$(MAKEFILE_LIST))),$(LOCAL_MODULE),$(strip $(LOCAL_MODULE_CLASS)),$(subst $(space),$(comma),$(sort $(LOCAL_MODULE_TAGS))) >> tag-list.csv)
+
+LOCAL_MODULE_TAGS := $(sort $(LOCAL_MODULE_TAGS))
+ifeq (,$(LOCAL_MODULE_TAGS))
+# Modules without tags fall back to user (which is changed to user eng below)
+LOCAL_MODULE_TAGS := user
+#$(warning default tags: $(lastword $(filter-out config/% out/%,$(MAKEFILE_LIST))))
+endif
+
+# Add implicit tags.
+#
+# If the local directory or one of its parents contains a MODULE_LICENSE_GPL
+# file, tag the module as "gnu". Search for "*_GNU*" so that we can also
+# find files like MODULE_LICENSE_GPL_AND_AFL but exclude files like
+# MODULE_LICENSE_LGPL.
+#
+ifneq ($(call find-parent-file,$(LOCAL_PATH),MODULE_LICENSE*_GPL*),)
+ LOCAL_MODULE_TAGS += gnu
+endif
+
+#
+# If this module is listed on CUSTOM_MODULES, promote it to "user"
+# so that it will be installed in $(TARGET_OUT).
+#
+ifneq (,$(filter $(LOCAL_MODULE),$(CUSTOM_MODULES)))
+ LOCAL_MODULE_TAGS := $(sort $(LOCAL_MODULE_TAGS) user)
+endif
+
+# The definition of should-install-to-system will be different depending
+# on which goal (e.g., user/eng/sdk) is being built.
+ifdef LOCAL_IS_HOST_MODULE
+ use_data :=
+else
+ use_data := $(if $(call should-install-to-system,$(LOCAL_MODULE_TAGS)),,_DATA)
+endif
+
+LOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS))
+ifneq ($(words $(LOCAL_MODULE_CLASS)),1)
+ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS must contain exactly one word, not "$(LOCAL_MODULE_CLASS)")
+endif
+
+# Add a tag like "_class@APPS" to this module so that we can filter
+# based on the class.
+LOCAL_MODULE_TAGS += _class@$(LOCAL_MODULE_CLASS)
+
+LOCAL_MODULE_PATH := $(strip $(LOCAL_MODULE_PATH))
+ifeq ($(LOCAL_MODULE_PATH),)
+ LOCAL_MODULE_PATH := $($(my_prefix)OUT$(use_data)_$(LOCAL_MODULE_CLASS))
+ ifeq ($(strip $(LOCAL_MODULE_PATH)),)
+ $(error $(LOCAL_PATH): unhandled LOCAL_MODULE_CLASS "$(LOCAL_MODULE_CLASS)")
+ endif
+endif
+
+ifneq ($(strip $(LOCAL_BUILT_MODULE)$(LOCAL_INSTALLED_MODULE)),)
+ $(error $(LOCAL_PATH): LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE must not be defined by component makefiles)
+endif
+
+# Make sure that this IS_HOST/CLASS/MODULE combination is unique.
+module_id := MODULE.$(if \
+ $(LOCAL_IS_HOST_MODULE),HOST,TARGET).$(LOCAL_MODULE_CLASS).$(LOCAL_MODULE)
+ifdef $(module_id)
+$(error $(LOCAL_PATH): $(module_id) already defined by $($(module_id)))
+endif
+$(module_id) := $(LOCAL_PATH)
+
+intermediates := $(call local-intermediates-dir)
+intermediates.COMMON := $(call local-intermediates-dir,COMMON)
+
+###########################################################
+# Pick a name for the intermediate and final targets
+###########################################################
+LOCAL_MODULE_STEM := $(strip $(LOCAL_MODULE_STEM))
+ifeq ($(LOCAL_MODULE_STEM),)
+ LOCAL_MODULE_STEM := $(LOCAL_MODULE)
+endif
+LOCAL_INSTALLED_MODULE_STEM := $(LOCAL_MODULE_STEM)$(LOCAL_MODULE_SUFFIX)
+
+LOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM))
+ifeq ($(LOCAL_BUILT_MODULE_STEM),)
+ LOCAL_BUILT_MODULE_STEM := $(LOCAL_INSTALLED_MODULE_STEM)
+endif
+
+# OVERRIDE_BUILT_MODULE_PATH is only allowed to be used by the
+# internal SHARED_LIBRARIES build files.
+OVERRIDE_BUILT_MODULE_PATH := $(strip $(OVERRIDE_BUILT_MODULE_PATH))
+ifdef OVERRIDE_BUILT_MODULE_PATH
+ ifneq ($(LOCAL_MODULE_CLASS),SHARED_LIBRARIES)
+ $(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
+ endif
+ built_module_path := $(OVERRIDE_BUILT_MODULE_PATH)
+else
+ built_module_path := $(intermediates)
+endif
+LOCAL_BUILT_MODULE := $(built_module_path)/$(LOCAL_BUILT_MODULE_STEM)
+built_module_path :=
+
+# LOCAL_UNINSTALLABLE_MODULE is only allowed to be used by the
+# internal STATIC_LIBRARIES build files.
+LOCAL_UNINSTALLABLE_MODULE := $(strip $(LOCAL_UNINSTALLABLE_MODULE))
+ifdef LOCAL_UNINSTALLABLE_MODULE
+ ifeq (,$(filter $(LOCAL_MODULE_CLASS),JAVA_LIBRARIES STATIC_LIBRARIES))
+ $(error $(LOCAL_PATH): Illegal use of LOCAL_UNINSTALLABLE_MODULE)
+ endif
+else
+ LOCAL_INSTALLED_MODULE := $(LOCAL_MODULE_PATH)/$(LOCAL_MODULE_SUBDIR)$(LOCAL_INSTALLED_MODULE_STEM)
+endif
+
+# Assemble the list of targets to create PRIVATE_ variables for.
+LOCAL_INTERMEDIATE_TARGETS += $(LOCAL_BUILT_MODULE)
+
+
+###########################################################
+## AIDL: Compile .aidl files to .java
+###########################################################
+
+aidl_sources := $(filter %.aidl,$(LOCAL_SRC_FILES))
+
+ifneq ($(strip $(aidl_sources)),)
+
+aidl_java_sources := $(patsubst %.aidl,%.java,$(addprefix $(intermediates.COMMON)/src/, $(aidl_sources)))
+aidl_sources := $(addprefix $(TOP_DIR)$(LOCAL_PATH)/, $(aidl_sources))
+
+$(aidl_java_sources): PRIVATE_AIDL_FLAGS := -b -I$(LOCAL_PATH) -I$(LOCAL_PATH)/src $(addprefix -I,$(LOCAL_AIDL_INCLUDES))
+
+$(aidl_java_sources): $(intermediates.COMMON)/src/%.java: $(TOPDIR)$(LOCAL_PATH)/%.aidl $(PRIVATE_ADDITIONAL_DEPENDENCIES) $(AIDL)
+ $(transform-aidl-to-java)
+-include $(aidl_java_sources:%.java=%.P)
+
+else
+aidl_java_sources :=
+endif
+
+###########################################################
+## Java: Compile .java files to .class
+###########################################################
+#TODO: pull this into java.make once host and target are combined
+
+java_sources := $(addprefix $(TOP_DIR)$(LOCAL_PATH)/, $(filter %.java,$(LOCAL_SRC_FILES))) $(aidl_java_sources)
+all_java_sources := $(java_sources) $(addprefix $($(my_prefix)OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES)))
+
+## Java resources #########################################
+
+# Look for resource files in any specified directories.
+# Non-java and non-doc files will be picked up as resources
+# and included in the output jar file.
+java_resource_file_groups :=
+
+LOCAL_JAVA_RESOURCE_DIRS := $(strip $(LOCAL_JAVA_RESOURCE_DIRS))
+ifneq ($(LOCAL_JAVA_RESOURCE_DIRS),)
+ # This makes a list of words like
+ # <dir1>::<file1>:<file2> <dir2>::<file1> <dir3>:
+ # where each of the files is relative to the directory it's grouped with.
+ # Directories that don't contain any resource files will result in groups
+ # that end with a colon, and they are stripped out in the next step.
+ java_resource_file_groups += \
+ $(foreach dir,$(LOCAL_JAVA_RESOURCE_DIRS), \
+ $(subst $(space),:,$(strip \
+ $(TOP_DIR)$(LOCAL_PATH)/$(dir): \
+ $(patsubst ./%,%,$(shell cd $(TOP_DIR)$(LOCAL_PATH)/$(dir) && \
+ find . \
+ -type d -a -name ".svn" -prune -o \
+ -type f \
+ -a \! -name "*.java" \
+ -a \! -name "package.html" \
+ -a \! -name "overview.html" \
+ -a \! -name ".*.swp" \
+ -a \! -name ".DS_Store" \
+ -a \! -name "*~" \
+ -print \
+ )) \
+ )) \
+ )
+ java_resource_file_groups := $(filter-out %:,$(java_resource_file_groups))
+endif # LOCAL_JAVA_RESOURCE_DIRS
+
+LOCAL_JAVA_RESOURCE_FILES := $(strip $(LOCAL_JAVA_RESOURCE_FILES))
+ifneq ($(LOCAL_JAVA_RESOURCE_FILES),)
+ java_resource_file_groups += \
+ $(foreach f,$(LOCAL_JAVA_RESOURCE_FILES), \
+ $(patsubst %/,%,$(dir $(f)))::$(notdir $(f)) \
+ )
+endif # LOCAL_JAVA_RESOURCE_FILES
+
+ifdef java_resource_file_groups
+ # The full paths to all resources, used for dependencies.
+ java_resource_sources := \
+ $(foreach group,$(java_resource_file_groups), \
+ $(addprefix $(word 1,$(subst :,$(space),$(group)))/, \
+ $(wordlist 2,9999,$(subst :,$(space),$(group))) \
+ ) \
+ )
+ # The arguments to jar that will include these files in a jar file.
+ extra_jar_args := \
+ $(foreach group,$(java_resource_file_groups), \
+ $(addprefix -C $(word 1,$(subst :,$(space),$(group))) , \
+ $(wordlist 2,9999,$(subst :,$(space),$(group))) \
+ ) \
+ )
+ java_resource_file_groups :=
+else
+ java_resource_sources :=
+ extra_jar_args :=
+endif # java_resource_file_groups
+
+## PRIVATE java vars ######################################
+
+ifneq ($(strip $(all_java_sources)$(all_res_assets)),)
+
+full_static_java_libs := \
+ $(foreach lib,$(LOCAL_STATIC_JAVA_LIBRARIES), \
+ $(call intermediates-dir-for, \
+ JAVA_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/javalib.jar)
+
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_INSTALL_DIR := $(dir $(LOCAL_INSTALLED_MODULE))
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_INTERMEDIATES_DIR := $(intermediates)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates)/classes
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates)/src
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_SOURCES := $(all_java_sources)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAVA_OBJECTS := $(patsubst %.java,%.class,$(LOCAL_SRC_FILES))
+ifeq ($(my_prefix),TARGET_)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_BOOTCLASSPATH := -bootclasspath $(call java-lib-files,core)
+endif
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_RESOURCE_DIR := $(LOCAL_RESOURCE_DIR)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_EXTRA_JAR_ARGS := $(extra_jar_args)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASSET_DIR := $(LOCAL_ASSET_DIR)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_STATIC_JAVA_LIBRARIES := $(full_static_java_libs)
+
+# full_java_libs: The list of files that should be used as the classpath.
+# Using this list as a dependency list WILL NOT WORK.
+# full_java_lib_deps: Should be specified as a prerequisite of this module
+# to guarantee that the files in full_java_libs will
+# be up-to-date.
+ifdef LOCAL_IS_HOST_MODULE
+# TODO: make prebuilt java libraries use the same
+# intermediates path pattern as target java libraries.
+full_java_libs := $(addprefix $(HOST_OUT_JAVA_LIBRARIES)/,$(addsuffix $(COMMON_JAVA_PACKAGE_SUFFIX),$(LOCAL_JAVA_LIBRARIES)))
+full_java_lib_deps := $(full_java_libs)
+else
+full_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
+full_java_lib_deps := $(call java-lib-deps,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
+endif
+full_java_libs += $(full_static_java_libs) $(LOCAL_CLASSPATH)
+full_java_lib_deps += $(full_static_java_libs) $(LOCAL_CLASSPATH)
+
+# This is set by packages that contain instrumentation, allowing them to
+# link against the package they are instrumenting. Currently only one such
+# package is allowed.
+LOCAL_INSTRUMENTATION_FOR := $(strip $(LOCAL_INSTRUMENTATION_FOR))
+ifdef LOCAL_INSTRUMENTATION_FOR
+ ifneq ($(words $(LOCAL_INSTRUMENTATION_FOR)),1)
+ $(error \
+ $(LOCAL_PATH): Multiple LOCAL_INSTRUMENTATION_FOR members defined)
+ endif
+
+ link_instr_intermediates_dir := $(call intermediates-dir-for, \
+ APPS,$(LOCAL_INSTRUMENTATION_FOR))
+ link_instr_intermediates_dir.COMMON := $(call intermediates-dir-for, \
+ APPS,$(LOCAL_INSTRUMENTATION_FOR),,COMMON)
+
+ full_java_libs += $(link_instr_intermediates_dir.COMMON)/classes.jar
+
+ # We can't depend on the .jar file, so we depend on something that
+ # depends on the jar file; the final built package file.
+ full_java_lib_deps += $(link_instr_intermediates_dir)/package.apk
+endif
+
+ifneq ($(strip $(LOCAL_JAR_MANIFEST)),)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST := $(LOCAL_PATH)/$(LOCAL_JAR_MANIFEST)
+else
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_JAR_MANIFEST :=
+endif
+
+endif
+
+
+###########################################################
+## make clean- targets
+###########################################################
+cleantarget := clean-$(LOCAL_MODULE)
+$(cleantarget) : PRIVATE_MODULE := $(LOCAL_MODULE)
+$(cleantarget) : PRIVATE_CLEAN_FILES := \
+ $(PRIVATE_CLEAN_FILES) \
+ $(LOCAL_BUILT_MODULE) \
+ $(LOCAL_INSTALLED_MODULE) \
+ $(intermediates)
+$(cleantarget)::
+ @echo "Clean: $(PRIVATE_MODULE)"
+ $(hide) rm -rf $(PRIVATE_CLEAN_FILES)
+
+###########################################################
+## Common definitions for module.
+###########################################################
+
+# Propagate local configuration options to this target.
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_PATH:=$(LOCAL_PATH)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_POST_PROCESS_COMMAND:= $(LOCAL_POST_PROCESS_COMMAND)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_AAPT_FLAGS:= $(LOCAL_AAPT_FLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_JAVA_LIBRARIES:= $(LOCAL_JAVA_LIBRARIES)
+#TODO: add this: $(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ADDITIONAL_DEPENDENCIES:= $(LOCAL_ADDITIONAL_DEPENDENCIES)
+
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_ALL_JAVA_LIBRARIES:= $(full_java_libs)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_IS_HOST_MODULE := $(LOCAL_IS_HOST_MODULE)
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_HOST:= $(my_host)
+
+# Tell the module and all of its sub-modules who it is.
+$(LOCAL_INTERMEDIATE_TARGETS) : PRIVATE_MODULE:= $(LOCAL_MODULE)
+
+# Provide a short-hand for building this module.
+# We name both BUILT and INSTALLED in case
+# LOCAL_UNINSTALLABLE_MODULE is set.
+.PHONY: $(LOCAL_MODULE)
+$(LOCAL_MODULE): $(LOCAL_BUILT_MODULE) $(LOCAL_INSTALLED_MODULE)
+
+###########################################################
+## Module installation rule
+###########################################################
+
+# Some hosts do not have ACP; override the LOCAL version if that's the case.
+ifneq ($(strip $(HOST_ACP_UNAVAILABLE)),)
+ LOCAL_ACP_UNAVAILABLE := $(strip $(HOST_ACP_UNAVAILABLE))
+endif
+
+ifndef LOCAL_UNINSTALLABLE_MODULE
+ # Define a copy rule to install the module.
+ # acp and libraries that it uses can't use acp for
+ # installation; hence, LOCAL_ACP_UNAVAILABLE.
+ifneq ($(LOCAL_ACP_UNAVAILABLE),true)
+$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE) | $(ACP)
+ @echo "Install: $@"
+ $(copy-file-to-target)
+else
+$(LOCAL_INSTALLED_MODULE): $(LOCAL_BUILT_MODULE)
+ @echo "Install: $@"
+ $(copy-file-to-target-with-cp)
+endif
+
+endif # !LOCAL_UNINSTALLABLE_MODULE
+
+###########################################################
+## Register with ALL_MODULES
+###########################################################
+
+ALL_MODULES += $(LOCAL_MODULE)
+
+# Don't use += on subvars, or else they'll end up being
+# recursively expanded.
+ALL_MODULES.$(LOCAL_MODULE).PATH := \
+ $(ALL_MODULES.$(LOCAL_MODULE).PATH) $(LOCAL_PATH)
+ALL_MODULES.$(LOCAL_MODULE).TAGS := \
+ $(ALL_MODULES.$(LOCAL_MODULE).TAGS) $(LOCAL_MODULE_TAGS)
+ALL_MODULES.$(LOCAL_MODULE).BUILT := \
+ $(ALL_MODULES.$(LOCAL_MODULE).BUILT) $(LOCAL_BUILT_MODULE)
+ALL_MODULES.$(LOCAL_MODULE).INSTALLED := \
+ $(ALL_MODULES.$(LOCAL_MODULE).INSTALLED) $(LOCAL_INSTALLED_MODULE)
+ALL_MODULES.$(LOCAL_MODULE).REQUIRED := \
+ $(ALL_MODULES.$(LOCAL_MODULE).REQUIRED) $(LOCAL_REQUIRED_MODULES)
+
+###########################################################
+## Take care of LOCAL_MODULE_TAGS
+###########################################################
+
+# Keep track of all the tags we've seen.
+ALL_MODULE_TAGS := $(sort $(ALL_MODULE_TAGS) $(LOCAL_MODULE_TAGS))
+
+# Add this module to the tag list of each specified tag.
+# Don't use "+=". If the variable hasn't been set with ":=",
+# it will default to recursive expansion.
+$(foreach tag,$(LOCAL_MODULE_TAGS),\
+ $(eval ALL_MODULE_TAGS.$(tag) := \
+ $(ALL_MODULE_TAGS.$(tag)) \
+ $(LOCAL_INSTALLED_MODULE)))
+
+# Add this module name to the tag list of each specified tag.
+$(foreach tag,$(LOCAL_MODULE_TAGS),\
+ $(eval ALL_MODULE_NAME_TAGS.$(tag) += $(LOCAL_MODULE)))
+
+# Always build everything, but only install a subset.
+ALL_BUILT_MODULES += $(LOCAL_BUILT_MODULE)
+
+###########################################################
+## NOTICE files
+###########################################################
+
+include $(BUILD_SYSTEM)/notice_files.mk
+
+#:vi noexpandtab
diff --git a/core/binary.mk b/core/binary.mk
new file mode 100644
index 000000000..0f35d3f0c
--- /dev/null
+++ b/core/binary.mk
@@ -0,0 +1,403 @@
+###########################################################
+## Standard rules for building binary object files from
+## asm/c/cpp/yacc/lex source files.
+##
+## The list of object files is exported in $(all_objects).
+###########################################################
+
+#######################################
+include $(BUILD_SYSTEM)/base_rules.mk
+#######################################
+
+###########################################################
+## Define PRIVATE_ variables used by multiple module types
+###########################################################
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_NO_DEFAULT_COMPILER_FLAGS := \
+ $(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS))
+
+ifeq ($(strip $(LOCAL_CC)),)
+ LOCAL_CC := $($(my_prefix)CC)
+endif
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CC := $(LOCAL_CC)
+
+ifeq ($(strip $(LOCAL_CXX)),)
+ LOCAL_CXX := $($(my_prefix)CXX)
+endif
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CXX := $(LOCAL_CXX)
+
+# TODO: support a mix of standard extensions so that this isn't necessary
+LOCAL_CPP_EXTENSION := $(strip $(LOCAL_CPP_EXTENSION))
+ifeq ($(LOCAL_CPP_EXTENSION),)
+ LOCAL_CPP_EXTENSION := .cpp
+endif
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPP_EXTENSION := $(LOCAL_CPP_EXTENSION)
+
+# Certain modules like libdl have to have symbols resolved at runtime and blow
+# up if --no-undefined is passed to the linker.
+ifeq ($(strip $(LOCAL_NO_DEFAULT_COMPILER_FLAGS)),)
+ifeq ($(strip $(LOCAL_ALLOW_UNDEFINED_SYMBOLS)),)
+ LOCAL_LDFLAGS := $(LOCAL_LDFLAGS) $($(my_prefix)NO_UNDEFINED_LDFLAGS)
+endif
+endif
+
+###########################################################
+## Define arm-vs-thumb-mode flags.
+###########################################################
+LOCAL_ARM_MODE := $(strip $(LOCAL_ARM_MODE))
+arm_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),arm)
+normal_objects_mode := $(if $(LOCAL_ARM_MODE),$(LOCAL_ARM_MODE),thumb)
+
+# Read the values from something like TARGET_arm_release_CFLAGS or
+# TARGET_thumb_debug_CFLAGS. HOST_(arm|thumb)_(release|debug)_CFLAGS
+# values aren't actually used (although they are usually empty).
+arm_objects_cflags := $($(my_prefix)$(arm_objects_mode)_$($(my_prefix)BUILD_TYPE)_CFLAGS)
+normal_objects_cflags := $($(my_prefix)$(normal_objects_mode)_$($(my_prefix)BUILD_TYPE)_CFLAGS)
+
+###########################################################
+## Define per-module debugging flags. Users can turn on
+## debugging for a particular module by setting DEBUG_MODULE_ModuleName
+## to a non-empty value in their environment or buildspec.mk,
+## and setting HOST_/TARGET_CUSTOM_DEBUG_CFLAGS to the
+## debug flags that they want to use.
+###########################################################
+ifdef DEBUG_MODULE_$(strip $(LOCAL_MODULE))
+ debug_cflags := $($(my_prefix)CUSTOM_DEBUG_CFLAGS)
+else
+ debug_cflags :=
+endif
+
+###########################################################
+## Stuff source generated from one-off tools
+###########################################################
+$(LOCAL_GENERATED_SOURCES): PRIVATE_MODULE := $(LOCAL_MODULE)
+
+ALL_GENERATED_SOURCES += $(LOCAL_GENERATED_SOURCES)
+
+
+###########################################################
+## YACC: Compile .y files to .cpp and the to .o.
+###########################################################
+
+yacc_sources := $(filter %.y,$(LOCAL_SRC_FILES))
+yacc_cpps := $(addprefix \
+ $(intermediates)/,$(yacc_sources:.y=$(LOCAL_CPP_EXTENSION)))
+yacc_headers := $(yacc_cpps:$(LOCAL_CPP_EXTENSION)=.h)
+yacc_objects := $(yacc_cpps:$(LOCAL_CPP_EXTENSION)=.o)
+
+ifneq ($(strip $(yacc_cpps)),)
+$(yacc_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \
+ $(TOPDIR)$(LOCAL_PATH)/%.y \
+ $(lex_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(call transform-y-to-cpp,$(PRIVATE_CPP_EXTENSION))
+$(yacc_headers): $(intermediates)/%.h: $(intermediates)/%$(LOCAL_CPP_EXTENSION)
+
+$(yacc_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(yacc_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+$(yacc_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION)
+ $(transform-$(PRIVATE_HOST)cpp-to-o)
+endif
+
+###########################################################
+## LEX: Compile .l files to .cpp and then to .o.
+###########################################################
+
+lex_sources := $(filter %.l,$(LOCAL_SRC_FILES))
+lex_cpps := $(addprefix \
+ $(intermediates)/,$(lex_sources:.l=$(LOCAL_CPP_EXTENSION)))
+lex_objects := $(lex_cpps:$(LOCAL_CPP_EXTENSION)=.o)
+
+ifneq ($(strip $(lex_cpps)),)
+$(lex_cpps): $(intermediates)/%$(LOCAL_CPP_EXTENSION): \
+ $(TOPDIR)$(LOCAL_PATH)/%.l
+ $(transform-l-to-cpp)
+
+$(lex_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(lex_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+$(lex_objects): $(intermediates)/%.o: \
+ $(intermediates)/%$(LOCAL_CPP_EXTENSION) \
+ $(PRIVATE_ADDITIONAL_DEPENDENCIES) \
+ $(yacc_headers)
+ $(transform-$(PRIVATE_HOST)cpp-to-o)
+endif
+
+###########################################################
+## C++: Compile .cpp files to .o.
+###########################################################
+
+# we also do this on host modules and sim builds, even though
+# it's not really arm, because there are files that are shared.
+cpp_arm_sources := $(patsubst %$(LOCAL_CPP_EXTENSION).arm,%$(LOCAL_CPP_EXTENSION),$(filter %$(LOCAL_CPP_EXTENSION).arm,$(LOCAL_SRC_FILES)))
+cpp_arm_objects := $(addprefix $(intermediates)/,$(cpp_arm_sources:$(LOCAL_CPP_EXTENSION)=.o))
+
+cpp_normal_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(LOCAL_SRC_FILES))
+cpp_normal_objects := $(addprefix $(intermediates)/,$(cpp_normal_sources:$(LOCAL_CPP_EXTENSION)=.o))
+
+$(cpp_arm_objects): PRIVATE_ARM_MODE := $(arm_objects_mode)
+$(cpp_arm_objects): PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)
+$(cpp_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(cpp_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+
+cpp_objects := $(cpp_arm_objects) $(cpp_normal_objects)
+
+ifneq ($(strip $(cpp_objects)),)
+$(cpp_objects): $(intermediates)/%.o: \
+ $(TOPDIR)$(LOCAL_PATH)/%$(LOCAL_CPP_EXTENSION) \
+ $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)cpp-to-o)
+-include $(cpp_objects:%.o=%.P)
+endif
+
+###########################################################
+## C++: Compile generated .cpp files to .o.
+###########################################################
+
+gen_cpp_sources := $(filter %$(LOCAL_CPP_EXTENSION),$(LOCAL_GENERATED_SOURCES))
+gen_cpp_objects := $(gen_cpp_sources:%$(LOCAL_CPP_EXTENSION)=%.o)
+
+ifneq ($(strip $(gen_cpp_objects)),)
+# Compile all generated files as thumb.
+# TODO: support compiling certain generated files as arm.
+$(gen_cpp_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(gen_cpp_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+$(gen_cpp_objects): $(intermediates)/%.o: $(intermediates)/%$(LOCAL_CPP_EXTENSION) $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)cpp-to-o)
+-include $(gen_cpp_objects:%.o=%.P)
+endif
+
+###########################################################
+## S: Compile generated .S and .s files to .o.
+###########################################################
+
+gen_S_sources := $(filter %.S,$(LOCAL_GENERATED_SOURCES))
+gen_S_objects := $(gen_S_sources:%.S=%.o)
+
+ifneq ($(strip $(gen_S_sources)),)
+$(gen_S_objects): $(intermediates)/%.o: $(intermediates)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)s-to-o)
+-include $(gen_S_objects:%.o=%.P)
+endif
+
+gen_s_sources := $(filter %.s,$(LOCAL_GENERATED_SOURCES))
+gen_s_objects := $(gen_s_sources:%.s=%.o)
+
+ifneq ($(strip $(gen_s_objects)),)
+$(gen_s_objects): $(intermediates)/%.o: $(intermediates)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)s-to-o-no-deps)
+-include $(gen_s_objects:%.o=%.P)
+endif
+
+gen_asm_objects := $(gen_S_objects) $(gen_s_objects)
+
+###########################################################
+## C: Compile .c files to .o.
+###########################################################
+
+c_arm_sources := $(patsubst %.c.arm,%.c,$(filter %.c.arm,$(LOCAL_SRC_FILES)))
+c_arm_objects := $(addprefix $(intermediates)/,$(c_arm_sources:.c=.o))
+
+c_normal_sources := $(filter %.c,$(LOCAL_SRC_FILES))
+c_normal_objects := $(addprefix $(intermediates)/,$(c_normal_sources:.c=.o))
+
+$(c_arm_objects): PRIVATE_ARM_MODE := $(arm_objects_mode)
+$(c_arm_objects): PRIVATE_ARM_CFLAGS := $(arm_objects_cflags)
+$(c_normal_objects): PRIVATE_ARM_MODE := $(normal_objects_mode)
+$(c_normal_objects): PRIVATE_ARM_CFLAGS := $(normal_objects_cflags)
+
+c_objects := $(c_arm_objects) $(c_normal_objects)
+
+ifneq ($(strip $(c_objects)),)
+$(c_objects): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.c $(yacc_cpps) $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)c-to-o)
+-include $(c_objects:%.o=%.P)
+endif
+
+###########################################################
+## AS: Compile .S files to .o.
+###########################################################
+
+asm_sources_S := $(filter %.S,$(LOCAL_SRC_FILES))
+asm_objects_S := $(addprefix $(intermediates)/,$(asm_sources_S:.S=.o))
+
+ifneq ($(strip $(asm_objects_S)),)
+$(asm_objects_S): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.S $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)s-to-o)
+-include $(asm_objects_S:%.o=%.P)
+endif
+
+asm_sources_s := $(filter %.s,$(LOCAL_SRC_FILES))
+asm_objects_s := $(addprefix $(intermediates)/,$(asm_sources_s:.s=.o))
+
+ifneq ($(strip $(asm_objects_s)),)
+$(asm_objects_s): $(intermediates)/%.o: $(TOPDIR)$(LOCAL_PATH)/%.s $(PRIVATE_ADDITIONAL_DEPENDENCIES)
+ $(transform-$(PRIVATE_HOST)s-to-o-no-deps)
+-include $(asm_objects_s:%.o=%.P)
+endif
+
+asm_objects := $(asm_objects_S) $(asm_objects_s)
+
+
+###########################################################
+## Common object handling.
+###########################################################
+
+# some rules depend on asm_objects being first. If your code depends on
+# being first, it's reasonable to require it to be assembly
+all_objects := \
+ $(asm_objects) \
+ $(cpp_objects) \
+ $(gen_cpp_objects) \
+ $(gen_asm_objects) \
+ $(c_objects) \
+ $(yacc_objects) \
+ $(lex_objects) \
+ $(addprefix $(TOPDIR)$(LOCAL_PATH)/,$(LOCAL_PREBUILT_OBJ_FILES))
+
+LOCAL_C_INCLUDES += $(TOPDIR)$(LOCAL_PATH) $(intermediates) $(base_intermediates)
+
+$(all_objects) : | $(LOCAL_GENERATED_SOURCES)
+ALL_C_CPP_ETC_OBJECTS += $(all_objects)
+
+###########################################################
+## Copy headers to the install tree
+###########################################################
+include $(BUILD_COPY_HEADERS)
+
+###########################################################
+# Standard library handling.
+#
+# On the target, we compile with -nostdlib, so we must add in the
+# default system shared libraries, unless they have requested not
+# to by supplying a LOCAL_SYSTEM_SHARED_LIBRARIES value. One would
+# supply that, for example, when building libc itself.
+###########################################################
+ifndef LOCAL_IS_HOST_MODULE
+ ifeq ($(LOCAL_SYSTEM_SHARED_LIBRARIES),none)
+ LOCAL_SHARED_LIBRARIES += $($(my_prefix)DEFAULT_SYSTEM_SHARED_LIBRARIES)
+ else
+ LOCAL_SHARED_LIBRARIES += $(LOCAL_SYSTEM_SHARED_LIBRARIES)
+ endif
+endif
+
+# Logging used to be part of libcutils (target) and libutils (sim);
+# hack modules that use those other libs to also include liblog.
+# All of this complexity is to make sure that liblog only appears
+# once, and appears just before libcutils or libutils on the link
+# line.
+# TODO: remove this hack and change all modules to use liblog
+# when necessary.
+define insert-liblog
+ $(if $(filter liblog,$(1)),$(1), \
+ $(if $(filter libcutils,$(1)), \
+ $(patsubst libcutils,liblog libcutils,$(1)) \
+ , \
+ $(patsubst libutils,liblog libutils,$(1)) \
+ ) \
+ )
+endef
+ifneq (,$(filter libcutils libutils,$(LOCAL_SHARED_LIBRARIES)))
+ LOCAL_SHARED_LIBRARIES := $(call insert-liblog,$(LOCAL_SHARED_LIBRARIES))
+endif
+ifneq (,$(filter libcutils libutils,$(LOCAL_STATIC_LIBRARIES)))
+ LOCAL_STATIC_LIBRARIES := $(call insert-liblog,$(LOCAL_STATIC_LIBRARIES))
+endif
+ifneq (,$(filter libcutils libutils,$(LOCAL_WHOLE_STATIC_LIBRARIES)))
+ LOCAL_WHOLE_STATIC_LIBRARIES := $(call insert-liblog,$(LOCAL_WHOLE_STATIC_LIBRARIES))
+endif
+
+###########################################################
+# The list of libraries that this module will link against are in
+# these variables. Each is a list of bare module names like "libc libm".
+#
+# LOCAL_SHARED_LIBRARIES
+# LOCAL_STATIC_LIBRARIES
+# LOCAL_WHOLE_STATIC_LIBRARIES
+#
+# We need to convert the bare names into the dependencies that
+# we'll use for LOCAL_BUILT_MODULE and LOCAL_INSTALLED_MODULE.
+# LOCAL_BUILT_MODULE should depend on the BUILT versions of the
+# libraries, so that simply building this module doesn't force
+# an install of a library. Similarly, LOCAL_INSTALLED_MODULE
+# should depend on the INSTALLED versions of the libraries so
+# that they get installed when this module does.
+###########################################################
+# NOTE:
+# WHOLE_STATIC_LIBRARIES are libraries that are pulled into the
+# module without leaving anything out, which is useful for turning
+# a collection of .a files into a .so file. Linking against a
+# normal STATIC_LIBRARY will only pull in code/symbols that are
+# referenced by the module. (see gcc/ld's --whole-archive option)
+###########################################################
+
+# Get the list of BUILT libraries, which are under
+# various intermediates directories.
+so_suffix := $($(my_prefix)SHLIB_SUFFIX)
+a_suffix := $($(my_prefix)STATIC_LIB_SUFFIX)
+
+built_shared_libraries := \
+ $(addprefix $($(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \
+ $(addsuffix $(so_suffix), \
+ $(LOCAL_SHARED_LIBRARIES)))
+
+built_static_libraries := \
+ $(foreach lib,$(LOCAL_STATIC_LIBRARIES), \
+ $(call intermediates-dir-for, \
+ STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/$(lib)$(a_suffix))
+
+built_whole_libraries := \
+ $(foreach lib,$(LOCAL_WHOLE_STATIC_LIBRARIES), \
+ $(call intermediates-dir-for, \
+ STATIC_LIBRARIES,$(lib),$(LOCAL_IS_HOST_MODULE))/$(lib)$(a_suffix))
+
+# Get the list of INSTALLED libraries. Strip off the various
+# intermediates directories and point to the common lib dirs.
+installed_shared_libraries := \
+ $(addprefix $($(my_prefix)OUT_SHARED_LIBRARIES)/, \
+ $(notdir $(built_shared_libraries)))
+
+# We don't care about installed static libraries, since the
+# libraries have already been linked into the module at that point.
+# We do, however, care about the NOTICE files for any static
+# libraries that we use. (see notice_files.make)
+
+installed_static_library_notice_file_targets := \
+ $(foreach lib,$(LOCAL_STATIC_LIBRARIES) $(LOCAL_WHOLE_STATIC_LIBRARIES), \
+ NOTICE-$(if $(LOCAL_IS_HOST_MODULE),HOST,TARGET)-STATIC_LIBRARIES-$(lib))
+
+###########################################################
+# Rule-specific variable definitions
+###########################################################
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_YACCFLAGS := $(LOCAL_YACCFLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ASFLAGS := $(LOCAL_ASFLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CFLAGS := $(LOCAL_CFLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_CPPFLAGS := $(LOCAL_CPPFLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_DEBUG_CFLAGS := $(debug_cflags)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_C_INCLUDES := $(LOCAL_C_INCLUDES)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDFLAGS := $(LOCAL_LDFLAGS)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_LDLIBS := $(LOCAL_LDLIBS)
+
+# this is really the way to get the files onto the command line instead
+# of using $^, because then LOCAL_ADDITIONAL_DEPENDENCIES doesn't work
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_SHARED_LIBRARIES := $(built_shared_libraries)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_STATIC_LIBRARIES := $(built_static_libraries)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_WHOLE_STATIC_LIBRARIES := $(built_whole_libraries)
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_ALL_OBJECTS := $(all_objects)
+
+###########################################################
+# Define library dependencies.
+###########################################################
+# all_libraries is used for the dependencies on LOCAL_BUILT_MODULE.
+all_libraries := \
+ $(built_shared_libraries) \
+ $(built_static_libraries) \
+ $(built_whole_libraries)
+
+# Make LOCAL_INSTALLED_MODULE depend on the installed versions of the
+# libraries so they get installed along with it. We don't need to
+# rebuild it when installing it, though, so this can be an order-only
+# dependency.
+$(LOCAL_INSTALLED_MODULE): | $(installed_shared_libraries)
+
+# Also depend on the notice files for any static libraries that
+# are linked into this module. This will force them to be installed
+# when this module is.
+$(LOCAL_INSTALLED_MODULE): | $(installed_static_library_notice_file_targets)
diff --git a/core/build_id.mk b/core/build_id.mk
new file mode 100644
index 000000000..cb18bc492
--- /dev/null
+++ b/core/build_id.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Defines branch-specific values.
+#
+
+# BUILD_ID is usually used to specify the branch name
+# (like "MAIN") or a branch name and a release candidate
+# (like "TC1-RC5"). It must be a single word, and is
+# capitalized by convention.
+#
+BUILD_ID := CUPCAKE
+
+# DISPLAY_BUILD_NUMBER should only be set for development branches,
+# If set, the BUILD_NUMBER (cl) is appended to the BUILD_ID for
+# a more descriptive BUILD_ID_DISPLAY, otherwise BUILD_ID_DISPLAY
+# is the same as BUILD_ID
+DISPLAY_BUILD_NUMBER := true
diff --git a/core/checktree b/core/checktree
new file mode 100755
index 000000000..b0b9cfab6
--- /dev/null
+++ b/core/checktree
@@ -0,0 +1,113 @@
+#!/usr/bin/python -E
+
+import sys, os, re
+
+excludes = [r'.*?/\.obj.*?',
+ r'.*?~',
+ r'.*?\/.DS_Store',
+ r'.*?\/.gdb_history',
+ r'.*?\/buildspec.mk',
+ r'.*?/\..*?\.swp',
+ r'.*?/out/.*?',
+ r'.*?/install/.*?']
+
+excludes_compiled = map(re.compile, excludes)
+
+def filter_excludes(str):
+ for e in excludes_compiled:
+ if e.match(str):
+ return False
+ return True
+
+def split_perforce_parts(s):
+ spaces = ((s.count(" ") + 1) / 3) * 2
+ pos = 0
+ while spaces > 0:
+ pos = s.find(" ", pos) + 1
+ spaces = spaces - 1
+ return s[pos:]
+
+def quotate(s):
+ return '"' + s + '"'
+
+class PerforceError(Exception):
+ def __init__(self,value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+
+def run(command, regex, filt):
+ def matchit(s):
+ m = regex_compiled.match(s)
+ if m:
+ return m.group(1)
+ else:
+ return ""
+ def filterit(s):
+ if filt_compiled.match(s):
+ return True
+ else:
+ return False
+
+ fd = os.popen(command);
+ lines = fd.readlines()
+ status = fd.close()
+ if status:
+ raise PerforceError("error calling " + command)
+
+ regex_compiled = re.compile(regex)
+ filt_compiled = re.compile(filt)
+
+ if len(lines) >= 1:
+ lines = filter(filterit, lines)
+ if len(lines) >= 1:
+ return map(matchit, lines)
+ return None
+
+try:
+ if len(sys.argv) == 1:
+ do_exclude = True
+ elif len(sys.argv) == 2 and sys.argv[1] == "-a":
+ do_exclude = False
+ else:
+ print "usage: checktree [-a]"
+ print " -a don't filter common crud in the tree"
+ sys.exit(1)
+
+ have = run("p4 have ...", r'[^#]+#[0-9]+ - (.*)', r'.*')
+
+ cwd = os.getcwd()
+ files = run("find . -not -type d", r'.(.*)', r'.*')
+ files = map(lambda s: cwd+s, files)
+
+ added_depot_path = run("p4 opened ...", r'([^#]+)#.*', r'.*?#[0-9]+ - add .*');
+ added = []
+ if added_depot_path:
+ added_depot_path = map(quotate, added_depot_path)
+
+ where = "p4 where " + " ".join(added_depot_path)
+ added = run(where, r'(.*)', r'.*')
+ added = map(split_perforce_parts, added)
+
+ extras = []
+
+ # Python 2.3 -- still default on Mac OS X -- does not have set()
+ # Make dict's here to support the "in" operations below
+ have = dict().fromkeys(have, 1)
+ added = dict().fromkeys(added, 1)
+
+ for file in files:
+ if not file in have:
+ if not file in added:
+ extras.append(file)
+
+ if do_exclude:
+ extras = filter(filter_excludes, extras)
+
+ for s in extras:
+ print s.replace(" ", "\\ ")
+
+except PerforceError, e:
+ sys.exit(2)
+
diff --git a/core/cleanbuild.mk b/core/cleanbuild.mk
new file mode 100644
index 000000000..50b56f952
--- /dev/null
+++ b/core/cleanbuild.mk
@@ -0,0 +1,204 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+INTERNAL_CLEAN_STEPS :=
+
+# Builds up a list of clean steps. Creates a unique
+# id for each step by taking INTERNAL_CLEAN_BUILD_VERSION
+# and appending an increasing number of '@' characters.
+#
+# $(1): shell command to run
+define _add-clean-step
+ $(if $(strip $(INTERNAL_CLEAN_BUILD_VERSION)),, \
+ $(error INTERNAL_CLEAN_BUILD_VERSION not set))
+ $(eval _acs_id := $(strip $(lastword $(INTERNAL_CLEAN_STEPS))))
+ $(if $(_acs_id),,$(eval _acs_id := $(INTERNAL_CLEAN_BUILD_VERSION)))
+ $(eval _acs_id := $(_acs_id)@)
+ $(eval INTERNAL_CLEAN_STEPS += $(_acs_id))
+ $(eval INTERNAL_CLEAN_STEP.$(_acs_id) := $(1))
+ $(eval _acs_id :=)
+endef
+define add-clean-step
+$(if $(call _add-clean-step,$(1)),)
+endef
+
+# Defines INTERNAL_CLEAN_BUILD_VERSION and the individual clean steps.
+# cleanspec.mk is outside of the core directory so that more people
+# can have permission to touch it.
+include build/cleanspec.mk
+INTERNAL_CLEAN_BUILD_VERSION := $(strip $(INTERNAL_CLEAN_BUILD_VERSION))
+
+# If the clean_steps.mk file is missing (usually after a clean build)
+# then we won't do anything.
+CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)
+CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)
+
+# Read the current state from the file, if present.
+# Will set CURRENT_CLEAN_BUILD_VERSION and CURRENT_CLEAN_STEPS.
+#
+clean_steps_file := $(PRODUCT_OUT)/clean_steps.mk
+-include $(clean_steps_file)
+
+ifneq ($(CURRENT_CLEAN_BUILD_VERSION),$(INTERNAL_CLEAN_BUILD_VERSION))
+ # The major clean version is out-of-date. Do a full clean, and
+ # don't even bother with the clean steps.
+ $(info *** A clean build is required because of a recent change.)
+ $(shell rm -rf $(OUT_DIR))
+ $(info *** Done with the cleaning, now starting the real build.)
+else
+ # The major clean version is correct. Find the list of clean steps
+ # that we need to execute to get up-to-date.
+ steps := \
+ $(filter-out $(CURRENT_CLEAN_STEPS),$(INTERNAL_CLEAN_STEPS))
+ $(foreach step,$(steps), \
+ $(info Clean step: $(INTERNAL_CLEAN_STEP.$(step))) \
+ $(shell $(INTERNAL_CLEAN_STEP.$(step))) \
+ )
+ steps :=
+endif
+CURRENT_CLEAN_BUILD_VERSION :=
+CURRENT_CLEAN_STEPS :=
+
+# Write the new state to the file.
+#
+$(shell \
+ mkdir -p $(dir $(clean_steps_file)) && \
+ echo "CURRENT_CLEAN_BUILD_VERSION := $(INTERNAL_CLEAN_BUILD_VERSION)" > \
+ $(clean_steps_file) ;\
+ echo "CURRENT_CLEAN_STEPS := $(INTERNAL_CLEAN_STEPS)" >> \
+ $(clean_steps_file) \
+ )
+
+clean_steps_file :=
+INTERNAL_CLEAN_STEPS :=
+INTERNAL_CLEAN_BUILD_VERSION :=
+
+
+# Since products and build variants (unfortunately) share the same
+# PRODUCT_OUT staging directory, things can get out of sync if different
+# build configurations are built in the same tree. The following logic
+# will notice when the configuration has changed and remove the files
+# necessary to keep things consistent.
+
+previous_build_config_file := $(PRODUCT_OUT)/previous_build_config.mk
+
+# TODO: this special case for the sdk is only necessary while "sdk"
+# is a valid make target. Eventually, it will just be a product, at
+# which point TARGET_PRODUCT will handle it and we can avoid this check
+# of MAKECMDGOALS. The "addprefix" is just to keep things pretty.
+ifneq ($(TARGET_PRODUCT),sdk)
+ building_sdk := $(addprefix -,$(filter sdk,$(MAKECMDGOALS)))
+else
+ # Don't bother with this extra part when explicitly building the sdk product.
+ building_sdk :=
+endif
+
+# A change in the list of locales warrants an installclean, too.
+locale_list := $(subst $(space),$(comma),$(strip $(PRODUCT_LOCALES)))
+
+current_build_config := \
+ $(TARGET_PRODUCT)-$(TARGET_BUILD_VARIANT)$(building_sdk)-{$(locale_list)}
+building_sdk :=
+locale_list :=
+force_installclean := false
+
+# Read the current state from the file, if present.
+# Will set PREVIOUS_BUILD_CONFIG.
+#
+PREVIOUS_BUILD_CONFIG :=
+-include $(previous_build_config_file)
+PREVIOUS_BUILD_CONFIG := $(strip $(PREVIOUS_BUILD_CONFIG))
+ifdef PREVIOUS_BUILD_CONFIG
+ ifneq "$(current_build_config)" "$(PREVIOUS_BUILD_CONFIG)"
+ $(info *** Build configuration changed: "$(PREVIOUS_BUILD_CONFIG)" -> "$(current_build_config)")
+ ifneq ($(DISABLE_AUTO_INSTALLCLEAN),true)
+ force_installclean := true
+ else
+ $(info DISABLE_AUTO_INSTALLCLEAN is set; skipping auto-clean. Your tree may be in an inconsistent state.)
+ endif
+ endif
+endif # else, this is the first build, so no need to clean.
+PREVIOUS_BUILD_CONFIG :=
+
+# Write the new state to the file.
+#
+$(shell \
+ mkdir -p $(dir $(previous_build_config_file)) && \
+ echo "PREVIOUS_BUILD_CONFIG := $(current_build_config)" > \
+ $(previous_build_config_file) \
+ )
+previous_build_config_file :=
+current_build_config :=
+
+#
+# installclean logic
+#
+
+# The files/dirs to delete during an installclean. This includes the
+# non-common APPS directory, which may contain the wrong resources.
+# Use "./" in front of the paths to avoid accidentally deleting random
+# parts of the filesystem if any of the *_OUT vars resolve to blank.
+#
+# Deletes all of the files that change between different build types,
+# like "make user" vs. "make sdk". This lets you work with different
+# build types without having to do a full clean each time. E.g.:
+#
+# $ make -j8 all
+# $ make installclean
+# $ make -j8 user
+# $ make installclean
+# $ make -j8 sdk
+#
+installclean_files := \
+ ./$(HOST_OUT)/obj/NOTICE_FILES \
+ ./$(HOST_OUT)/sdk \
+ ./$(PRODUCT_OUT)/*.img \
+ ./$(PRODUCT_OUT)/*.txt \
+ ./$(PRODUCT_OUT)/*.xlb \
+ ./$(PRODUCT_OUT)/*.zip \
+ ./$(PRODUCT_OUT)/data \
+ ./$(PRODUCT_OUT)/obj/APPS \
+ ./$(PRODUCT_OUT)/obj/NOTICE_FILES \
+ ./$(PRODUCT_OUT)/obj/PACKAGING \
+ ./$(PRODUCT_OUT)/recovery \
+ ./$(PRODUCT_OUT)/root \
+ ./$(PRODUCT_OUT)/system
+
+# The files/dirs to delete during a dataclean, which removes any files
+# in the staging and emulator data partitions.
+dataclean_files := \
+ ./$(PRODUCT_OUT)/data/* \
+ ./$(PRODUCT_OUT)/data-qemu/* \
+ ./$(PRODUCT_OUT)/userdata-qemu.img
+
+# Define the rules for commandline invocation.
+.PHONY: dataclean
+dataclean: FILES := $(dataclean_files)
+dataclean:
+ $(hide) rm -rf $(FILES)
+ @echo "Deleted emulator userdata images."
+
+.PHONY: installclean
+installclean: FILES := $(installclean_files)
+installclean: dataclean
+ $(hide) rm -rf $(FILES)
+ @echo "Deleted images and staging directories."
+
+ifeq "$(force_installclean)" "true"
+ $(info *** Forcing "make installclean"...)
+ $(shell rm -rf $(dataclean_files) $(installclean_files))
+ $(info *** Done with the cleaning, now starting the real build.)
+endif
+force_installclean :=
diff --git a/core/clear_vars.mk b/core/clear_vars.mk
new file mode 100644
index 000000000..f09050785
--- /dev/null
+++ b/core/clear_vars.mk
@@ -0,0 +1,91 @@
+###########################################################
+## Clear out values of all variables used by rule templates.
+###########################################################
+
+LOCAL_MODULE:=
+LOCAL_MODULE_PATH:=
+LOCAL_MODULE_STEM:=
+LOCAL_BUILT_MODULE:=
+LOCAL_BUILT_MODULE_STEM:=
+OVERRIDE_BUILT_MODULE_PATH:=
+LOCAL_INSTALLED_MODULE:=
+LOCAL_UNINSTALLABLE_MODULE:=
+LOCAL_INTERMEDIATE_TARGETS:=
+LOCAL_UNSTRIPPED_PATH:=
+LOCAL_MODULE_CLASS:=
+LOCAL_MODULE_SUFFIX:=
+LOCAL_PACKAGE_NAME:=
+LOCAL_OVERRIDES_PACKAGES:=
+LOCAL_EXPORT_PACKAGE_RESOURCES:=
+LOCAL_REQUIRED_MODULES:=
+LOCAL_ACP_UNAVAILABLE:=
+LOCAL_MODULE_TAGS:=
+LOCAL_SRC_FILES:=
+LOCAL_PREBUILT_OBJ_FILES:=
+LOCAL_STATIC_JAVA_LIBRARIES:=
+LOCAL_STATIC_LIBRARIES:=
+LOCAL_WHOLE_STATIC_LIBRARIES:=
+LOCAL_SHARED_LIBRARIES:=
+LOCAL_IS_HOST_MODULE:=
+LOCAL_CC:=
+LOCAL_CXX:=
+LOCAL_CPP_EXTENSION:=
+LOCAL_NO_DEFAULT_COMPILER_FLAGS:=
+LOCAL_ARM_MODE:=
+LOCAL_YACCFLAGS:=
+LOCAL_ASFLAGS:=
+LOCAL_CFLAGS:=
+LOCAL_CPPFLAGS:=
+LOCAL_C_INCLUDES:=
+LOCAL_LDFLAGS:=
+LOCAL_LDLIBS:=
+LOCAL_AAPT_FLAGS:=
+LOCAL_SYSTEM_SHARED_LIBRARIES:=none
+LOCAL_PREBUILT_LIBS:=
+LOCAL_PREBUILT_EXECUTABLES:=
+LOCAL_PREBUILT_JAVA_LIBRARIES:=
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES:=
+LOCAL_INTERMEDIATE_SOURCES:=
+LOCAL_JAVA_LIBRARIES:=
+LOCAL_NO_STANDARD_LIBRARIES:=
+LOCAL_CLASSPATH:=
+LOCAL_DROIDDOC_SOURCE_PATH:=
+LOCAL_DROIDDOC_TEMPLATE_DIR:=
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=
+LOCAL_DROIDDOC_ASSET_DIR:=
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=
+LOCAL_DROIDDOC_OPTIONS:=
+LOCAL_DROIDDOC_HTML_DIR:=
+LOCAL_ASSET_FILES:=
+LOCAL_ASSET_DIR:=
+LOCAL_RESOURCE_DIR:=
+LOCAL_JAVA_RESOURCE_DIRS:=
+LOCAL_JAVA_RESOURCE_FILES:=
+LOCAL_GENERATED_SOURCES:=
+LOCAL_COPY_HEADERS_TO:=
+LOCAL_COPY_HEADERS:=
+LOCAL_FORCE_STATIC_EXECUTABLE:=
+LOCAL_ADDITIONAL_DEPENDENCIES:=
+LOCAL_PRELINK_MODULE:=
+LOCAL_COMPRESS_MODULE_SYMBOLS:=
+LOCAL_STRIP_MODULE:=
+LOCAL_POST_PROCESS_COMMAND:=true
+LOCAL_JNI_SHARED_LIBRARIES:=
+LOCAL_JAR_MANIFEST:=
+LOCAL_INSTRUMENTATION_FOR:=
+LOCAL_INSTRUMENTATION_FOR_PACKAGE_NAME:=
+LOCAL_AIDL_INCLUDES:=
+LOCAL_JARJAR_RULES:=
+LOCAL_ADDITIONAL_JAVA_DIR:=
+LOCAL_ALLOW_UNDEFINED_SYMBOLS:=
+LOCAL_DX_FLAGS:=
+LOCAL_CERTIFICATE:=
+LOCAL_SDK_VERSION:=
+LOCAL_NO_EMMA_INSTRUMENT:=
+LOCAL_NO_EMMA_COMPILE:=
+
+# Trim MAKEFILE_LIST so that $(call my-dir) doesn't need to
+# iterate over thousands of entries every time.
+# Leave the current makefile to make sure we don't break anything
+# that expects to be able to find the name of the current makefile.
+MAKEFILE_LIST := $(lastword $(MAKEFILE_LIST))
diff --git a/core/combo/darwin-x86.mk b/core/combo/darwin-x86.mk
new file mode 100644
index 000000000..21509609b
--- /dev/null
+++ b/core/combo/darwin-x86.mk
@@ -0,0 +1,97 @@
+# Configuration for Darwin (Mac OS X) on PPC.
+# Included by combo/select.make
+
+$(combo_target)GLOBAL_CFLAGS += -fPIC
+$(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,-undefined,error
+
+$(combo_target)CC := $(CC)
+$(combo_target)CXX := $(CXX)
+$(combo_target)AR := $(AR)
+
+$(combo_target)SHLIB_SUFFIX := .dylib
+$(combo_target)JNILIB_SUFFIX := .jnilib
+
+$(combo_target)GLOBAL_CFLAGS += \
+ -include $(call select-android-config-h,darwin-x86)
+$(combo_target)RUN_RANLIB_AFTER_COPYING := true
+
+ifeq ($(combo_target),TARGET_)
+$(combo_target)CUSTOM_LD_COMMAND := true
+define transform-o-to-shared-lib-inner
+ $(TARGET_CXX) \
+ -dynamiclib -single_module -read_only_relocs suppress \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(PRIVATE_LDLIBS) \
+ -o $@ \
+ $(PRIVATE_LDFLAGS) \
+ $(if $(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES),-all_load) \
+ $(TARGET_LIBGCC)
+endef
+
+define transform-o-to-executable-inner
+ $(TARGET_CXX) \
+ -o $@ \
+ -Wl,-dynamic -headerpad_max_install_names \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(PRIVATE_LDLIBS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(TARGET_LIBGCC)
+endef
+
+define transform-o-to-static-executable-inner
+ $(TARGET_CXX) \
+ -static \
+ -o $@ \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(PRIVATE_LDFLAGS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(PRIVATE_LDLIBS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(TARGET_LIBGCC)
+endef
+
+else
+$(combo_target)CUSTOM_LD_COMMAND := true
+
+define transform-host-o-to-shared-lib-inner
+ $(HOST_CXX) \
+ -dynamiclib -single_module -read_only_relocs suppress \
+ $(HOST_GLOBAL_LD_DIRS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(PRIVATE_LDLIBS) \
+ -o $@ \
+ $(PRIVATE_LDFLAGS) \
+ $(HOST_LIBGCC)
+endef
+
+define transform-host-o-to-executable-inner
+$(HOST_CXX) \
+ -o $@ \
+ -Wl,-dynamic -headerpad_max_install_names \
+ $(HOST_GLOBAL_LD_DIRS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(PRIVATE_LDLIBS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(HOST_LIBGCC)
+endef
+
+# $(1): The file to check
+define get-file-size
+stat -f "%z" $(1)
+endef
+
+endif
+
diff --git a/core/combo/javac.mk b/core/combo/javac.mk
new file mode 100644
index 000000000..d4c04e726
--- /dev/null
+++ b/core/combo/javac.mk
@@ -0,0 +1,37 @@
+# Selects a Java compiler.
+#
+# Inputs:
+# CUSTOM_JAVA_COMPILER -- "eclipse", "openjdk". or nothing for the system
+# default
+#
+# Outputs:
+# COMMON_JAVAC -- Java compiler command with common arguments
+
+# Whatever compiler is on this system.
+ifeq ($(HOST_OS), windows)
+ COMMON_JAVAC := development/host/windows/prebuilt/javawrap.exe -J-Xmx256m \
+ -target 1.5 -Xmaxerrs 9999999
+else
+ COMMON_JAVAC := javac -J-Xmx512M -target 1.5 -Xmaxerrs 9999999
+endif
+
+# Eclipse.
+ifeq ($(CUSTOM_JAVA_COMPILER), eclipse)
+ COMMON_JAVAC := java -Xmx256m -jar prebuilt/common/ecj/ecj.jar -5 \
+ -maxProblems 9999999 -nowarn
+ $(info CUSTOM_JAVA_COMPILER=eclipse)
+endif
+
+# OpenJDK.
+ifeq ($(CUSTOM_JAVA_COMPILER), openjdk)
+ # We set the VM options (like -Xmx) in the javac script.
+ COMMON_JAVAC := prebuilt/common/openjdk/bin/javac -target 1.5 \
+ -Xmaxerrs 9999999
+ $(info CUSTOM_JAVA_COMPILER=openjdk)
+endif
+
+HOST_JAVAC ?= $(COMMON_JAVAC)
+TARGET_JAVAC ?= $(COMMON_JAVAC)
+
+#$(info HOST_JAVAC=$(HOST_JAVAC))
+#$(info TARGET_JAVAC=$(TARGET_JAVAC))
diff --git a/core/combo/linux-arm.mk b/core/combo/linux-arm.mk
new file mode 100644
index 000000000..507e4dd9c
--- /dev/null
+++ b/core/combo/linux-arm.mk
@@ -0,0 +1,154 @@
+# Configuration for Linux on ARM.
+# Included by combo/select.make
+
+# You can set TARGET_TOOLS_PREFIX to get gcc from somewhere else
+ifeq ($(strip $($(combo_target)TOOLS_PREFIX)),)
+$(combo_target)TOOLS_PREFIX := \
+ prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin/arm-eabi-
+endif
+
+$(combo_target)CC := $($(combo_target)TOOLS_PREFIX)gcc$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)CXX := $($(combo_target)TOOLS_PREFIX)g++$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)AR := $($(combo_target)TOOLS_PREFIX)ar$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)OBJCOPY := $($(combo_target)TOOLS_PREFIX)objcopy$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)LD := $($(combo_target)TOOLS_PREFIX)ld$(HOST_EXECUTABLE_SUFFIX)
+
+$(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,--no-undefined
+
+TARGET_arm_release_CFLAGS := -fomit-frame-pointer \
+ -fstrict-aliasing \
+ -funswitch-loops \
+ -finline-limit=300
+
+TARGET_thumb_release_CFLAGS := -mthumb \
+ -Os \
+ -fomit-frame-pointer \
+ -fno-strict-aliasing \
+ -finline-limit=64
+
+# When building for debug, compile everything as arm.
+TARGET_arm_debug_CFLAGS := $(TARGET_arm_release_CFLAGS) -fno-omit-frame-pointer -fno-strict-aliasing
+TARGET_thumb_debug_CFLAGS := $(TARGET_thumb_release_CFLAGS) -marm -fno-omit-frame-pointer
+
+# NOTE: if you try to build a debug build with thumb, several
+# of the libraries (libpv, libwebcore, libkjs) need to be built
+# with -mlong-calls. When built at -O0, those libraries are
+# too big for a thumb "BL <label>" to go from one end to the other.
+
+## As hopefully a temporary hack,
+## use this to force a full ARM build (for easier debugging in gdb)
+## (don't forget to do a clean build)
+##TARGET_arm_release_CFLAGS := $(TARGET_arm_release_CFLAGS) -fno-omit-frame-pointer
+##TARGET_thumb_release_CFLAGS := $(TARGET_thumb_release_CFLAGS) -marm -fno-omit-frame-pointer
+
+## on some hosts, the target cross-compiler is not available so do not run this command
+ifneq ($(wildcard $($(combo_target)CC)),)
+$(combo_target)LIBGCC := $(shell $($(combo_target)CC) -mthumb-interwork -print-libgcc-file-name)
+endif
+
+$(combo_target)GLOBAL_CFLAGS += \
+ -march=armv5te -mtune=xscale \
+ -msoft-float -fpic \
+ -mthumb-interwork \
+ -ffunction-sections \
+ -funwind-tables \
+ -fstack-protector \
+ -fno-short-enums \
+ -D__ARM_ARCH_5__ -D__ARM_ARCH_5T__ \
+ -D__ARM_ARCH_5E__ -D__ARM_ARCH_5TE__ \
+ -include $(call select-android-config-h,linux-arm)
+
+$(combo_target)GLOBAL_CPPFLAGS += -fvisibility-inlines-hidden
+
+$(combo_target)RELEASE_CFLAGS := \
+ -DSK_RELEASE -DNDEBUG \
+ -O2 -g \
+ -Wstrict-aliasing=2 \
+ -finline-functions \
+ -fno-inline-functions-called-once \
+ -fgcse-after-reload \
+ -frerun-cse-after-loop \
+ -frename-registers
+
+libc_root := bionic/libc
+libm_root := bionic/libm
+libstdc++_root := bionic/libstdc++
+libthread_db_root := bionic/libthread_db
+
+# unless CUSTOM_KERNEL_HEADERS is defined, we're going to use
+# symlinks located in out/ to point to the appropriate kernel
+# headers. see 'config/kernel_headers.make' for more details
+#
+ifneq ($(CUSTOM_KERNEL_HEADERS),)
+ KERNEL_HEADERS_COMMON := $(CUSTOM_KERNEL_HEADERS)
+ KERNEL_HEADERS_ARCH := $(CUSTOM_KERNEL_HEADERS)
+else
+ KERNEL_HEADERS_COMMON := $(libc_root)/kernel/common
+ KERNEL_HEADERS_ARCH := $(libc_root)/kernel/arch-$(TARGET_ARCH)
+endif
+KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
+
+$(combo_target)C_INCLUDES := \
+ $(libc_root)/arch-arm/include \
+ $(libc_root)/include \
+ $(libstdc++_root)/include \
+ $(KERNEL_HEADERS) \
+ $(libm_root)/include \
+ $(libm_root)/include/arch/arm \
+ $(libthread_db_root)/include
+
+TARGET_CRTBEGIN_STATIC_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtbegin_static.o
+TARGET_CRTBEGIN_DYNAMIC_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtbegin_dynamic.o
+TARGET_CRTEND_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtend_android.o
+
+TARGET_STRIP_MODULE:=true
+
+$(combo_target)DEFAULT_SYSTEM_SHARED_LIBRARIES := libc libstdc++ libm
+
+$(combo_target)CUSTOM_LD_COMMAND := true
+define transform-o-to-shared-lib-inner
+$(TARGET_CXX) \
+ -nostdlib -Wl,-soname,$(notdir $@) -Wl,-T,$(BUILD_SYSTEM)/armelf.xsc \
+ -Wl,--gc-sections \
+ -Wl,-shared,-Bsymbolic \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_LIBGCC)
+endef
+
+define transform-o-to-executable-inner
+$(TARGET_CXX) -nostdlib -Bdynamic -Wl,-T,$(BUILD_SYSTEM)/armelf.x \
+ -Wl,-dynamic-linker,/system/bin/linker \
+ -Wl,--gc-sections \
+ -Wl,-z,nocopyreloc \
+ -o $@ \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(TARGET_CRTBEGIN_DYNAMIC_O) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_LIBGCC) \
+ $(TARGET_CRTEND_O)
+endef
+
+define transform-o-to-static-executable-inner
+$(TARGET_CXX) -nostdlib -Bstatic -Wl,-T,$(BUILD_SYSTEM)/armelf.x \
+ -Wl,--gc-sections \
+ -o $@ \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(TARGET_CRTBEGIN_STATIC_O) \
+ $(PRIVATE_LDFLAGS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(TARGET_LIBGCC) \
+ $(TARGET_CRTEND_O)
+endef
diff --git a/core/combo/linux-x86.mk b/core/combo/linux-x86.mk
new file mode 100644
index 000000000..372c63e1d
--- /dev/null
+++ b/core/combo/linux-x86.mk
@@ -0,0 +1,33 @@
+# Configuration for Linux on x86.
+# Included by combo/select.make
+
+# right now we get these from the environment, but we should
+# pick them from the tree somewhere
+$(combo_target)CC := $(CC)
+$(combo_target)CXX := $(CXX)
+$(combo_target)AR := $(AR)
+
+ifeq ($(combo_target),HOST_)
+# $(1): The file to check
+define get-file-size
+stat --format "%s" "$(1)"
+endef
+endif
+
+# On the sim, we build the "host" tools in 64 bit iff the compiler
+# does it for us automatically. In other words, that means on 64 bit
+# system, they're 64 bit and on 32 bit systems, they're 32 bits. In
+# all other cases, we build 32 bit, since this is what we release.
+ifneq ($(combo_target)$(TARGET_SIMULATOR),HOST_true)
+$(combo_target)GLOBAL_CFLAGS := $($(combo_target)GLOBAL_CFLAGS) -m32
+$(combo_target)GLOBAL_LDFLAGS := $($(combo_target)GLOBAL_LDFLAGS) -m32
+endif
+
+
+$(combo_target)GLOBAL_CFLAGS += -fPIC
+$(combo_target)GLOBAL_CFLAGS += \
+ -include $(call select-android-config-h,linux-x86)
+
+$(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,--no-undefined
+
+
diff --git a/core/combo/select.mk b/core/combo/select.mk
new file mode 100644
index 000000000..c54da2273
--- /dev/null
+++ b/core/combo/select.mk
@@ -0,0 +1,81 @@
+# Select a combo based on the compiler being used.
+#
+# Inputs:
+# combo_target -- prefix for final variables (HOST_ or TARGET_)
+#
+# Outputs:
+# $(combo_target)OS -- standard name for this host (LINUX, DARWIN, etc.)
+# $(combo_target)ARCH -- standard name for process architecture (powerpc, x86, etc.)
+# $(combo_target)GLOBAL_CFLAGS -- C compiler flags to use for everything
+# $(combo_target)DEBUG_CFLAGS -- additional C compiler flags for debug builds
+# $(combo_target)RELEASE_CFLAGS -- additional C compiler flags for release builds
+# $(combo_target)GLOBAL_ARFLAGS -- flags to use for static linking everything
+# $(combo_target)SHLIB_SUFFIX -- suffix of shared libraries
+
+# Build a target string like "linux-arm" or "darwin-x86".
+combo_os_arch := $($(combo_target)OS)-$($(combo_target)ARCH)
+
+# Set the defaults.
+
+HOST_CC ?= $(CC)
+HOST_CXX ?= $(CXX)
+HOST_AR ?= $(AR)
+
+$(combo_target)BINDER_MINI := 0
+
+$(combo_target)HAVE_EXCEPTIONS := 0
+$(combo_target)HAVE_UNIX_FILE_PATH := 1
+$(combo_target)HAVE_WINDOWS_FILE_PATH := 0
+$(combo_target)HAVE_RTTI := 1
+$(combo_target)HAVE_CALL_STACKS := 1
+$(combo_target)HAVE_64BIT_IO := 1
+$(combo_target)HAVE_CLOCK_TIMERS := 1
+$(combo_target)HAVE_PTHREAD_RWLOCK := 1
+$(combo_target)HAVE_STRNLEN := 1
+$(combo_target)HAVE_STRERROR_R_STRRET := 1
+$(combo_target)HAVE_STRLCPY := 0
+$(combo_target)HAVE_STRLCAT := 0
+$(combo_target)HAVE_KERNEL_MODULES := 0
+
+# These flags might (will) be overridden by the target makefiles
+$(combo_target)GLOBAL_CFLAGS := -fno-exceptions -Wno-multichar
+$(combo_target)DEBUG_CFLAGS := -O0 -g
+$(combo_target)RELEASE_CFLAGS := -O2 -g -fno-strict-aliasing
+$(combo_target)GLOBAL_ARFLAGS := crs
+
+$(combo_target)EXECUTABLE_SUFFIX :=
+$(combo_target)SHLIB_SUFFIX := .so
+$(combo_target)JNILIB_SUFFIX := $($(combo_target)SHLIB_SUFFIX)
+$(combo_target)STATIC_LIB_SUFFIX := .a
+
+$(combo_target)PRELINKER_MAP := $(BUILD_SYSTEM)/prelink-$(combo_os_arch).map
+
+# We try to find a target or host specific file for the os/arch specified, and
+# default to just looking for the os/arch one. This will allow us to define
+# things separately for targets and hosts that have the same architecture
+# but need different defines. e.g. target_linux-x86 and host_linux-x86
+
+ifneq ($(TARGET_SIMULATOR),true)
+# Convert the combo_target string to lowercase
+combo_target_lc := $(shell echo $(combo_target) | tr '[A-Z]' '[a-z]')
+
+# form combo makefile name like "<path>/target_linux-x86.make",
+# "<path>/host_darwin-x86.make", etc.
+combo_target_os_arch := $(BUILD_COMBOS)/$(combo_target_lc)$(combo_os_arch).mk
+else
+combo_target_os_arch :=
+endif
+
+# Now include the combo for this specific target.
+ifneq ($(wildcard $(combo_target_os_arch)),)
+include $(combo_target_os_arch)
+else
+include $(BUILD_COMBOS)/$(combo_os_arch).mk
+endif
+
+ifneq ($(USE_CCACHE),)
+ ccache := prebuilt/$(HOST_PREBUILT_TAG)/ccache/ccache
+ $(combo_target)CC := $(ccache) $($(combo_target)CC)
+ $(combo_target)CXX := $(ccache) $($(combo_target)CXX)
+ ccache =
+endif
diff --git a/core/combo/target_linux-x86.mk b/core/combo/target_linux-x86.mk
new file mode 100644
index 000000000..2d359ff81
--- /dev/null
+++ b/core/combo/target_linux-x86.mk
@@ -0,0 +1,129 @@
+# Configuration for Linux on x86 as a target.
+# Included by combo/select.make
+
+# You can set TARGET_TOOLS_PREFIX to get gcc from somewhere else
+ifeq ($(strip $($(combo_target)TOOLS_PREFIX)),)
+$(combo_target)TOOLS_PREFIX := \
+ prebuilt/$(HOST_PREBUILT_TAG)/toolchain/i686-unknown-linux-gnu-4.2.1/bin/i686-unknown-linux-gnu-
+endif
+
+$(combo_target)CC := $($(combo_target)TOOLS_PREFIX)gcc$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)CXX := $($(combo_target)TOOLS_PREFIX)g++$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)AR := $($(combo_target)TOOLS_PREFIX)ar$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)OBJCOPY := $($(combo_target)TOOLS_PREFIX)objcopy$(HOST_EXECUTABLE_SUFFIX)
+$(combo_target)LD := $($(combo_target)TOOLS_PREFIX)ld$(HOST_EXECUTABLE_SUFFIX)
+
+ifneq ($(wildcard $($(combo_target)CC)),)
+$(combo_target)LIBGCC := \
+ $(shell $($(combo_target)CC) -m32 -print-file-name=libgcc.a) \
+ $(shell $($(combo_target)CC) -m32 -print-file-name=libgcc_eh.a)
+endif
+
+$(combo_target)NO_UNDEFINED_LDFLAGS := -Wl,--no-undefined
+
+libc_root := bionic/libc
+libm_root := bionic/libm
+libstdc++_root := bionic/libstdc++
+libthread_db_root := bionic/libthread_db
+
+# unless CUSTOM_KERNEL_HEADERS is defined, we're going to use
+# symlinks located in out/ to point to the appropriate kernel
+# headers. see 'config/kernel_headers.make' for more details
+#
+ifneq ($(CUSTOM_KERNEL_HEADERS),)
+ KERNEL_HEADERS_COMMON := $(CUSTOM_KERNEL_HEADERS)
+ KERNEL_HEADERS_ARCH := $(CUSTOM_KERNEL_HEADERS)
+else
+ KERNEL_HEADERS_COMMON := $(libc_root)/kernel/common
+ KERNEL_HEADERS_ARCH := $(libc_root)/kernel/arch-$(TARGET_ARCH)
+endif
+KERNEL_HEADERS := $(KERNEL_HEADERS_COMMON) $(KERNEL_HEADERS_ARCH)
+
+$(combo_target)GLOBAL_CFLAGS += \
+ -march=i686 \
+ -m32 \
+ -fPIC \
+ -include $(call select-android-config-h,target_linux-x86)
+
+$(combo_target)GLOBAL_CPPFLAGS += \
+ -fno-use-cxa-atexit
+
+$(combo_target)C_INCLUDES := \
+ $(libc_root)/arch-x86/include \
+ $(libc_root)/include \
+ $(libstdc++_root)/include \
+ $(KERNEL_HEADERS) \
+ $(libm_root)/include \
+ $(libm_root)/include/i387 \
+ $(libthread_db_root)/include
+
+TARGET_CRTBEGIN_STATIC_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtbegin_static.o
+TARGET_CRTBEGIN_DYNAMIC_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtbegin_dynamic.o
+TARGET_CRTEND_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtend_android.o
+
+
+TARGET_CRTBEGIN_SO_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtbegin_so.o
+TARGET_CRTEND_SO_O := $(TARGET_OUT_STATIC_LIBRARIES)/crtend_so.o
+
+# TARGET_STRIP_MODULE:=true
+
+$(combo_target)DEFAULT_SYSTEM_SHARED_LIBRARIES := libc libstdc++ libm
+
+$(combo_target)CUSTOM_LD_COMMAND := true
+define transform-o-to-shared-lib-inner
+$(TARGET_CXX) \
+ $(TARGET_GLOBAL_LDFLAGS) \
+ -nostdlib -Wl,-soname,$(notdir $@) \
+ -shared -Bsymbolic \
+ -fPIC -march=i686 \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(TARGET_CRTBEGIN_SO_O) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_LIBGCC) \
+ $(TARGET_CRTEND_SO_O)
+endef
+
+
+define transform-o-to-executable-inner
+$(TARGET_CXX) \
+ $(TARGET_GLOBAL_LDFLAGS) \
+ -nostdlib -Bdynamic \
+ -Wl,-dynamic-linker,/system/bin/linker \
+ -Wl,-z,nocopyreloc \
+ -o $@ \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ $(TARGET_CRTBEGIN_DYNAMIC_O) \
+ $(PRIVATE_ALL_OBJECTS) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_LIBGCC) \
+ $(TARGET_CRTEND_O)
+endef
+
+define transform-o-to-static-executable-inner
+$(TARGET_CXX) \
+ $(TARGET_GLOBAL_LDFLAGS) \
+ -nostdlib -Bstatic \
+ -o $@ \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(TARGET_CRTBEGIN_STATIC_O) \
+ $(PRIVATE_LDFLAGS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--start-group \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(TARGET_LIBGCC) \
+ -Wl,--end-group \
+ $(TARGET_CRTEND_O)
+endef
+
+$(combo_target)GLOBAL_CFLAGS += -m32
+$(combo_target)GLOBAL_LDFLAGS += -m32
diff --git a/core/combo/windows-x86.mk b/core/combo/windows-x86.mk
new file mode 100644
index 000000000..e32a07725
--- /dev/null
+++ b/core/combo/windows-x86.mk
@@ -0,0 +1,53 @@
+# Configuration for Linux on x86.
+# Included by combo/select.make
+
+# right now we get these from the environment, but we should
+# pick them from the tree somewhere
+TOOLS_PREFIX := #prebuilt/windows/host/bin/
+TOOLS_EXE_SUFFIX := .exe
+
+# Settings to use MinGW has a cross-compiler under Linux
+ifneq ($(findstring Linux,$(UNAME)),)
+ifneq ($(strip $(USE_MINGW)),)
+HOST_ACP_UNAVAILABLE := true
+TOOLS_PREFIX := /usr/bin/i586-mingw32msvc-
+TOOLS_EXE_SUFFIX :=
+$(combo_target)GLOBAL_CFLAGS += -DUSE_MINGW
+$(combo_target)C_INCLUDES += /usr/lib/gcc/i586-mingw32msvc/3.4.4/include
+$(combo_target)GLOBAL_LD_DIRS += -L/usr/i586-mingw32msvc/lib
+endif
+endif
+
+$(combo_target)CC := $(TOOLS_PREFIX)gcc$(TOOLS_EXE_SUFFIX)
+$(combo_target)CXX := $(TOOLS_PREFIX)g++$(TOOLS_EXE_SUFFIX)
+$(combo_target)AR := $(TOOLS_PREFIX)ar$(TOOLS_EXE_SUFFIX)
+
+$(combo_target)GLOBAL_CFLAGS += -include $(call select-android-config-h,windows)
+$(combo_target)GLOBAL_LDFLAGS += --enable-stdcall-fixup
+
+# when building under Cygwin, ensure that we use Mingw compilation by default.
+# you can disable this (i.e. to generate Cygwin executables) by defining the
+# USE_CYGWIN variable in your environment, e.g.:
+#
+# export USE_CYGWIN=1
+#
+# note that the -mno-cygwin flags are not needed when cross-compiling the
+# Windows host tools on Linux
+#
+ifneq ($(findstring CYGWIN,$(UNAME)),)
+ifeq ($(strip $(USE_CYGWIN)),)
+$(combo_target)GLOBAL_CFLAGS += -mno-cygwin
+$(combo_target)GLOBAL_LDFLAGS += -mno-cygwin -mconsole
+endif
+endif
+
+$(combo_target)SHLIB_SUFFIX := .dll
+$(combo_target)EXECUTABLE_SUFFIX := .exe
+
+ifeq ($(combo_target),HOST_)
+# $(1): The file to check
+# TODO: find out what format cygwin's stat(1) uses
+define get-file-size
+999999999
+endef
+endif
diff --git a/core/config.mk b/core/config.mk
new file mode 100644
index 000000000..90a40a731
--- /dev/null
+++ b/core/config.mk
@@ -0,0 +1,310 @@
+# This is included by the top-level Makefile.
+# It sets up standard variables based on the
+# current configuration and platform, which
+# are not specific to what is being built.
+
+# Use bash, not whatever shell somebody has installed as /bin/sh
+# This is repeated from main.mk, since envsetup.sh runs this file
+# directly.
+SHELL := /bin/bash
+
+# Standard source directories.
+SRC_DOCS:= $(TOPDIR)docs
+# TODO: Enforce some kind of layering; only add include paths
+# when a module links against a particular library.
+# TODO: See if we can remove most of these from the global list.
+SRC_HEADERS := \
+ $(TOPDIR)system/core/include \
+ $(TOPDIR)hardware/libhardware/include \
+ $(TOPDIR)hardware/libhardware_legacy/include \
+ $(TOPDIR)hardware/ril/include \
+ $(TOPDIR)dalvik/libnativehelper/include \
+ $(TOPDIR)frameworks/base/include \
+ $(TOPDIR)frameworks/base/opengl/include \
+ $(TOPDIR)external/skia/include
+SRC_HOST_HEADERS:=$(TOPDIR)tools/include
+SRC_LIBRARIES:= $(TOPDIR)libs
+SRC_SERVERS:= $(TOPDIR)servers
+SRC_TARGET_DIR := $(TOPDIR)build/target
+SRC_API_DIR := $(TOPDIR)frameworks/base/api
+
+# Some specific paths to tools
+SRC_DROIDDOC_DIR := $(TOPDIR)build/tools/droiddoc
+
+# Various mappings to avoid hard-coding paths all over the place
+include $(BUILD_SYSTEM)/pathmap.mk
+
+# ###############################################################
+# Build system internal files
+# ###############################################################
+
+BUILD_COMBOS:= $(BUILD_SYSTEM)/combo
+
+CLEAR_VARS:= $(BUILD_SYSTEM)/clear_vars.mk
+BUILD_HOST_STATIC_LIBRARY:= $(BUILD_SYSTEM)/host_static_library.mk
+BUILD_HOST_SHARED_LIBRARY:= $(BUILD_SYSTEM)/host_shared_library.mk
+BUILD_STATIC_LIBRARY:= $(BUILD_SYSTEM)/static_library.mk
+BUILD_RAW_STATIC_LIBRARY := $(BUILD_SYSTEM)/raw_static_library.mk
+BUILD_SHARED_LIBRARY:= $(BUILD_SYSTEM)/shared_library.mk
+BUILD_EXECUTABLE:= $(BUILD_SYSTEM)/executable.mk
+BUILD_RAW_EXECUTABLE:= $(BUILD_SYSTEM)/raw_executable.mk
+BUILD_HOST_EXECUTABLE:= $(BUILD_SYSTEM)/host_executable.mk
+BUILD_PACKAGE:= $(BUILD_SYSTEM)/package.mk
+BUILD_HOST_PREBUILT:= $(BUILD_SYSTEM)/host_prebuilt.mk
+BUILD_PREBUILT:= $(BUILD_SYSTEM)/prebuilt.mk
+BUILD_MULTI_PREBUILT:= $(BUILD_SYSTEM)/multi_prebuilt.mk
+BUILD_JAVA_LIBRARY:= $(BUILD_SYSTEM)/java_library.mk
+BUILD_STATIC_JAVA_LIBRARY:= $(BUILD_SYSTEM)/static_java_library.mk
+BUILD_HOST_JAVA_LIBRARY:= $(BUILD_SYSTEM)/host_java_library.mk
+BUILD_DROIDDOC:= $(BUILD_SYSTEM)/droiddoc.mk
+BUILD_COPY_HEADERS := $(BUILD_SYSTEM)/copy_headers.mk
+BUILD_KEY_CHAR_MAP := $(BUILD_SYSTEM)/key_char_map.mk
+
+# ###############################################################
+# Parse out any modifier targets.
+# ###############################################################
+
+# The 'showcommands' goal says to show the full command
+# lines being executed, instead of a short message about
+# the kind of operation being done.
+SHOW_COMMANDS:= $(filter showcommands,$(MAKECMDGOALS))
+
+
+# ###############################################################
+# Set common values
+# ###############################################################
+
+# These can be changed to modify both host and device modules.
+COMMON_GLOBAL_CFLAGS:= -DANDROID -fmessage-length=0 -W -Wall -Wno-unused
+COMMON_DEBUG_CFLAGS:=
+COMMON_RELEASE_CFLAGS:= -DNDEBUG -UDEBUG
+
+COMMON_GLOBAL_CPPFLAGS:=
+COMMON_DEBUG_CPPFLAGS:=
+COMMON_RELEASE_CPPFLAGS:=
+
+# Set the extensions used for various packages
+COMMON_PACKAGE_SUFFIX := .zip
+COMMON_JAVA_PACKAGE_SUFFIX := .jar
+COMMON_ANDROID_PACKAGE_SUFFIX := .apk
+
+# list of flags to turn specific warnings in to errors
+TARGET_ERROR_FLAGS := -Werror=return-type
+
+# ###############################################################
+# Include sub-configuration files
+# ###############################################################
+
+# ---------------------------------------------------------------
+# Try to include buildspec.mk, which will try to set stuff up.
+# If this file doesn't exist, the environemnt variables will
+# be used, and if that doesn't work, then the default is an
+# arm build
+-include $(TOPDIR)buildspec.mk
+
+# ---------------------------------------------------------------
+# Define most of the global variables. These are the ones that
+# are specific to the user's build configuration.
+include $(BUILD_SYSTEM)/envsetup.mk
+
+# $(1): os/arch
+define select-android-config-h
+system/core/include/arch/$(1)/AndroidConfig.h
+endef
+
+combo_target := HOST_
+include $(BUILD_SYSTEM)/combo/select.mk
+
+# on windows, the tools have .exe at the end, and we depend on the
+# host config stuff being done first
+
+combo_target := TARGET_
+include $(BUILD_SYSTEM)/combo/select.mk
+
+# Pick a Java compiler.
+include $(BUILD_SYSTEM)/combo/javac.mk
+
+# ---------------------------------------------------------------
+# Check that the configuration is current. We check that
+# BUILD_ENV_SEQUENCE_NUMBER is current against this value.
+# Don't fail if we're called from envsetup, so they have a
+# chance to update their environment.
+
+ifeq (,$(strip $(CALLED_FROM_SETUP)))
+ifneq (,$(strip $(BUILD_ENV_SEQUENCE_NUMBER)))
+ifneq ($(BUILD_ENV_SEQUENCE_NUMBER),$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER))
+$(warning BUILD_ENV_SEQUENCE_NUMBER is set incorrectly.)
+$(info *** If you use envsetup/lunch/choosecombo:)
+$(info *** - Re-execute envsetup (". envsetup.sh"))
+$(info *** - Re-run lunch or choosecombo)
+$(info *** If you use buildspec.mk:)
+$(info *** - Look at buildspec.mk.default to see what has changed)
+$(info *** - Update BUILD_ENV_SEQUENCE_NUMBER to "$(CORRECT_BUILD_ENV_SEQUENCE_NUMBER)")
+$(error bailing..)
+endif
+endif
+endif
+
+
+# ---------------------------------------------------------------
+# Generic tools.
+
+LEX:= flex
+YACC:= bison -d
+DOXYGEN:= doxygen
+AAPT := $(HOST_OUT_EXECUTABLES)/aapt$(HOST_EXECUTABLE_SUFFIX)
+ACP := $(HOST_OUT_EXECUTABLES)/acp$(HOST_EXECUTABLE_SUFFIX)
+AIDL := $(HOST_OUT_EXECUTABLES)/aidl$(HOST_EXECUTABLE_SUFFIX)
+ICUDATA := $(HOST_OUT_EXECUTABLES)/icudata$(HOST_EXECUTABLE_SUFFIX)
+SIGNAPK_JAR := $(HOST_OUT_JAVA_LIBRARIES)/signapk$(COMMON_JAVA_PACKAGE_SUFFIX)
+MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
+MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
+MKYAFFS2 := $(HOST_OUT_EXECUTABLES)/mkyaffs2image$(HOST_EXECUTABLE_SUFFIX)
+APICHECK := $(HOST_OUT_EXECUTABLES)/apicheck$(HOST_EXECUTABLE_SUFFIX)
+FS_GET_STATS := $(HOST_OUT_EXECUTABLES)/fs_get_stats$(HOST_EXECUTABLE_SUFFIX)
+MKEXT2IMG := $(HOST_OUT_EXECUTABLES)/genext2fs$(HOST_EXECUTABLE_SUFFIX)
+MKEXT2BOOTIMG := external/genext2fs/mkbootimg_ext2.sh
+MKTARBALL := build/tools/mktarball.sh
+TUNE2FS := tune2fs
+E2FSCK := e2fsck
+JARJAR := java -jar $(HOST_OUT_JAVA_LIBRARIES)/jarjar.jar
+
+# dx is java behind a shell script; no .exe necessary.
+DX := $(HOST_OUT_EXECUTABLES)/dx
+KCM := $(HOST_OUT_EXECUTABLES)/kcm$(HOST_EXECUTABLE_SUFFIX)
+ZIPALIGN := $(HOST_OUT_EXECUTABLES)/zipalign$(HOST_EXECUTABLE_SUFFIX)
+FINDBUGS := prebuilt/common/findbugs/bin/findbugs
+LOCALIZE := $(HOST_OUT_EXECUTABLES)/localize$(HOST_EXECUTABLE_SUFFIX)
+EMMA_JAR := external/emma/lib/emma$(COMMON_JAVA_PACKAGE_SUFFIX)
+
+# Binary prelinker/compressor tools
+APRIORI := $(HOST_OUT_EXECUTABLES)/apriori$(HOST_EXECUTABLE_SUFFIX)
+LSD := $(HOST_OUT_EXECUTABLES)/lsd$(HOST_EXECUTABLE_SUFFIX)
+SOSLIM := $(HOST_OUT_EXECUTABLES)/soslim$(HOST_EXECUTABLE_SUFFIX)
+
+# Deal with archaic version of bison on Mac OS X.
+ifeq ($(filter 1.28,$(shell $(YACC) -V)),)
+YACC_HEADER_SUFFIX:= .hpp
+else
+YACC_HEADER_SUFFIX:= .cpp.h
+endif
+
+# Don't use column under Windows, cygwin or not
+ifeq ($(HOST_OS),windows)
+COLUMN:= cat
+else
+COLUMN:= column
+endif
+
+dir := $(shell uname)
+ifeq ($(HOST_OS),windows)
+dir := $(HOST_OS)
+endif
+ifeq ($(HOST_OS),darwin)
+dir := $(HOST_OS)-$(HOST_ARCH)
+endif
+OLD_FLEX := prebuilt/$(HOST_PREBUILT_TAG)/flex/flex-2.5.4a$(HOST_EXECUTABLE_SUFFIX)
+
+ifeq ($(HOST_OS),darwin)
+# Mac OS' screwy version of java uses a non-standard directory layout
+# and doesn't even seem to have tools.jar. On the other hand, javac seems
+# to be able to magically find the classes in there, wherever they are, so
+# leave this blank
+HOST_JDK_TOOLS_JAR :=
+else
+HOST_JDK_TOOLS_JAR:= $(shell $(BUILD_SYSTEM)/find-jdk-tools-jar.sh)
+endif
+
+# It's called md5 on Mac OS and md5sum on Linux
+ifeq ($(HOST_OS),darwin)
+MD5SUM:=md5 -q
+else
+MD5SUM:=md5sum
+endif
+
+# ###############################################################
+# Set up final options.
+# ###############################################################
+
+HOST_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
+HOST_DEBUG_CFLAGS += $(COMMON_DEBUG_CFLAGS)
+HOST_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
+
+HOST_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
+HOST_DEBUG_CPPFLAGS += $(COMMON_DEBUG_CPPFLAGS)
+HOST_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
+
+TARGET_GLOBAL_CFLAGS += $(COMMON_GLOBAL_CFLAGS)
+TARGET_DEBUG_CFLAGS += $(COMMON_DEBUG_CFLAGS)
+TARGET_RELEASE_CFLAGS += $(COMMON_RELEASE_CFLAGS)
+
+TARGET_GLOBAL_CPPFLAGS += $(COMMON_GLOBAL_CPPFLAGS)
+TARGET_DEBUG_CPPFLAGS += $(COMMON_DEBUG_CPPFLAGS)
+TARGET_RELEASE_CPPFLAGS += $(COMMON_RELEASE_CPPFLAGS)
+
+HOST_GLOBAL_LD_DIRS += -L$(HOST_OUT_INTERMEDIATE_LIBRARIES)
+TARGET_GLOBAL_LD_DIRS += -L$(TARGET_OUT_INTERMEDIATE_LIBRARIES)
+
+HOST_PROJECT_INCLUDES:= $(SRC_HEADERS) $(SRC_HOST_HEADERS) $(HOST_OUT_HEADERS)
+TARGET_PROJECT_INCLUDES:= $(SRC_HEADERS) $(TARGET_OUT_HEADERS)
+
+# Many host compilers don't support these flags, so we have to make
+# sure to only specify them for the target compilers checked in to
+# the source tree. The simulator uses the target flags but the
+# host compiler, so only set them for the target when the target
+# is not the simulator.
+ifneq ($(TARGET_SIMULATOR),true)
+TARGET_GLOBAL_CFLAGS += $(TARGET_ERROR_FLAGS)
+TARGET_GLOBAL_CPPFLAGS += $(TARGET_ERROR_FLAGS)
+endif
+
+ifeq ($(HOST_BUILD_TYPE),release)
+HOST_GLOBAL_CFLAGS+= $(HOST_RELEASE_CFLAGS)
+HOST_GLOBAL_CPPFLAGS+= $(HOST_RELEASE_CPPFLAGS)
+else
+HOST_GLOBAL_CFLAGS+= $(HOST_DEBUG_CFLAGS)
+HOST_GLOBAL_CPPFLAGS+= $(HOST_DEBUG_CPPFLAGS)
+endif
+
+ifeq ($(TARGET_BUILD_TYPE),release)
+TARGET_GLOBAL_CFLAGS+= $(TARGET_RELEASE_CFLAGS)
+TARGET_GLOBAL_CPPFLAGS+= $(TARGET_RELEASE_CPPFLAGS)
+else
+TARGET_GLOBAL_CFLAGS+= $(TARGET_DEBUG_CFLAGS)
+TARGET_GLOBAL_CPPFLAGS+= $(TARGET_DEBUG_CPPFLAGS)
+endif
+
+# TODO: do symbol compression
+TARGET_COMPRESS_MODULE_SYMBOLS := false
+TARGET_PRELINK_MODULE := true
+
+PREBUILT_IS_PRESENT := $(if $(wildcard prebuilt/Android.mk),true)
+
+
+# ###############################################################
+# Collect a list of the SDK versions that we could compile against
+# For use with the LOCAL_SDK_VERSION variable for include $(BUILD_PACKAGE)
+# ###############################################################
+
+# The files that we can convert into android.jars are are in config/api/*.xml
+# The 'current' version is whatever this source tree is. Once the apicheck
+# tool can generate the stubs from the xml files, we'll use that to be
+# able to build back-versions. In the meantime, 'current' is the only
+# one supported.
+#
+# sgrax is the opposite of xargs. It takes the list of args and puts them
+# on each line for sort to process.
+# sort -g is a numeric sort, so 1 2 3 10 instead of 1 10 2 3.
+TARGET_AVAILABLE_SDK_VERSIONS := current \
+ $(shell function sgrax() { \
+ while [ -n "$$1" ] ; do echo $$1 ; shift ; done \
+ } ; \
+ ( sgrax $(patsubst $(SRC_API_DIR)/%.xml,%, \
+ $(filter-out $(SRC_API_DIR)/current.xml, \
+ $(shell find $(SRC_API_DIR) -name "*.xml"))) | sort -g ) )
+
+
+INTERNAL_PLATFORM_API_FILE := $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/public_api.xml
+
+
+
diff --git a/core/copy_headers.mk b/core/copy_headers.mk
new file mode 100644
index 000000000..dac07d522
--- /dev/null
+++ b/core/copy_headers.mk
@@ -0,0 +1,23 @@
+###########################################################
+## Copy headers to the install tree
+###########################################################
+ifneq ($(strip $(LOCAL_IS_HOST_MODULE)),)
+ my_prefix := HOST_
+else
+ my_prefix := TARGET_
+endif
+
+# Create a rule to copy each header, and make the
+# all_copied_headers phony target depend on each
+# destination header. copy-one-header defines the
+# actual rule.
+#
+$(foreach header,$(LOCAL_COPY_HEADERS), \
+ $(eval _chFrom := $(LOCAL_PATH)/$(header)) \
+ $(eval _chTo := \
+ $($(my_prefix)OUT_HEADERS)/$(LOCAL_COPY_HEADERS_TO)/$(notdir $(header))) \
+ $(eval $(call copy-one-header,$(_chFrom),$(_chTo))) \
+ $(eval all_copied_headers: $(_chTo)) \
+ )
+_chFrom :=
+_chTo :=
diff --git a/core/definitions.mk b/core/definitions.mk
new file mode 100644
index 000000000..3efef8d87
--- /dev/null
+++ b/core/definitions.mk
@@ -0,0 +1,1488 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##
+## Common build system definitions. Mostly standard
+## commands for building various types of targets, which
+## are used by others to construct the final targets.
+##
+
+# These are variables we use to collect overall lists
+# of things being processed.
+
+# Full paths to all of the documentation
+ALL_DOCS:=
+
+# The short names of all of the targets in the system.
+# For each element of ALL_MODULES, two other variables
+# are defined:
+# $(ALL_MODULES.$(target)).BUILT
+# $(ALL_MODULES.$(target)).INSTALLED
+# The BUILT variable contains LOCAL_BUILT_MODULE for that
+# target, and the INSTALLED variable contains the LOCAL_INSTALLED_MODULE.
+# Some targets may have multiple files listed in the BUILT and INSTALLED
+# sub-variables.
+ALL_MODULES:=
+
+# Full paths to targets that should be added to the "make droid"
+# set of installed targets.
+ALL_DEFAULT_INSTALLED_MODULES:=
+
+# Full paths to all targets that will be built.
+ALL_BUILT_MODULES:=
+
+# The list of tags that have been defined by
+# LOCAL_MODULE_TAGS. Each word in this variable maps
+# to a corresponding ALL_MODULE_TAGS.<tagname> variable
+# that contains all of the INSTALLED_MODULEs with that tag.
+ALL_MODULE_TAGS:=
+
+# Similar to ALL_MODULE_TAGS, but contains the short names
+# of all targets for a particular tag. The top-level variable
+# won't have the list of tags; ust ALL_MODULE_TAGS to get
+# the list of all known tags. (This means that this variable
+# will always be empty; it's just here as a placeholder for
+# its sub-variables.)
+ALL_MODULE_NAME_TAGS:=
+
+# Full paths to all prebuilt files that will be copied
+# (used to make the dependency on acp)
+ALL_PREBUILT:=
+
+# Full path to all files that are made by some tool
+ALL_GENERATED_SOURCES:=
+
+# Full path to all asm, C, C++, lex and yacc generated C files.
+# These all have an order-only dependency on the copied headers
+ALL_C_CPP_ETC_OBJECTS:=
+
+# The list of dynamic binaries that haven't been stripped/compressed/prelinked.
+ALL_ORIGINAL_DYNAMIC_BINARIES:=
+
+# These files go into the SDK
+ALL_SDK_FILES:=
+
+# Files for dalvik. This is often build without building the rest of the OS.
+INTERNAL_DALVIK_MODULES:=
+
+# All findbugs xml files
+ALL_FINDBUGS_FILES:=
+
+###########################################################
+## Debugging; prints a variable list to stdout
+###########################################################
+
+# $(1): variable name list, not variable values
+define print-vars
+$(foreach var,$(1), \
+ $(info $(var):) \
+ $(foreach word,$($(var)), \
+ $(info $(space)$(space)$(word)) \
+ ) \
+ )
+endef
+
+###########################################################
+## Retrieve the directory of the current makefile
+###########################################################
+
+# Figure out where we are.
+define my-dir
+$(patsubst %/,%,$(dir $(lastword $(MAKEFILE_LIST),$(MAKEFILE_LIST))))
+endef
+
+###########################################################
+## Retrieve a list of all makefiles immediately below some directory
+###########################################################
+
+define all-makefiles-under
+$(wildcard $(1)/*/Android.mk)
+endef
+
+###########################################################
+## Look under a directory for makefiles that don't have parent
+## makefiles.
+###########################################################
+
+# $(1): directory to search under
+# Ignores $(1)/Android.mk
+define first-makefiles-under
+$(shell build/tools/findleaves.sh --mindepth=2 $(1) Android.mk)
+endef
+
+###########################################################
+## Retrieve a list of all makefiles immediately below your directory
+###########################################################
+
+define all-subdir-makefiles
+$(call all-makefiles-under,$(call my-dir))
+endef
+
+###########################################################
+## Look in the named list of directories for makefiles,
+## relative to the current directory.
+###########################################################
+
+# $(1): List of directories to look for under this directory
+define all-named-subdir-makefiles
+$(wildcard $(addsuffix /Android.mk, $(addprefix $(my-dir)/,$(1))))
+endef
+
+###########################################################
+## Find all of the java files under the named directories.
+## Meant to be used like:
+## SRC_FILES := $(call all-java-files-under,src tests)
+###########################################################
+
+define all-java-files-under
+$(patsubst ./%,%, \
+ $(shell cd $(LOCAL_PATH) ; \
+ find $(1) -name "*.java" -and -not -name ".*") \
+ )
+endef
+
+###########################################################
+## Find all of the java files from here. Meant to be used like:
+## SRC_FILES := $(call all-subdir-java-files)
+###########################################################
+
+define all-subdir-java-files
+$(call all-java-files-under,.)
+endef
+
+###########################################################
+## Find all of the c files under the named directories.
+## Meant to be used like:
+## SRC_FILES := $(call all-c-files-under,src tests)
+###########################################################
+
+define all-c-files-under
+$(patsubst ./%,%, \
+ $(shell cd $(LOCAL_PATH) ; \
+ find $(1) -name "*.c" -and -not -name ".*") \
+ )
+endef
+
+###########################################################
+## Find all of the c files from here. Meant to be used like:
+## SRC_FILES := $(call all-subdir-c-files)
+###########################################################
+
+define all-subdir-c-files
+$(call all-c-files-under,.)
+endef
+
+###########################################################
+## Find all files named "I*.aidl" under the named directories,
+## which must be relative to $(LOCAL_PATH). The returned list
+## is relative to $(LOCAL_PATH).
+###########################################################
+
+define all-Iaidl-files-under
+$(patsubst ./%,%, \
+ $(shell cd $(LOCAL_PATH) ; \
+ find $(1) -name "I*.aidl" -and -not -name ".*") \
+ )
+endef
+
+###########################################################
+## Find all of the "I*.aidl" files under $(LOCAL_PATH).
+###########################################################
+
+define all-subdir-Iaidl-files
+$(call all-Iaidl-files-under,.)
+endef
+
+###########################################################
+## Find all of the html files from here. Meant to be used like:
+## SRC_FILES := $(call all-subdir-html-files)
+###########################################################
+
+define all-subdir-html-files
+$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find . -name "*.html"))
+endef
+
+###########################################################
+## Find all of the files matching pattern
+## SRC_FILES := $(call find-subdir-files, <pattern>)
+###########################################################
+
+define find-subdir-files
+$(patsubst ./%,%,$(shell cd $(LOCAL_PATH) ; find $(1)))
+endef
+
+###########################################################
+# find the files in the subdirectory $1 of LOCAL_DIR
+# matching pattern $2, filtering out files $3
+# e.g.
+# SRC_FILES += $(call find-subdir-subdir-files, \
+# css, *.cpp, DontWantThis.cpp)
+###########################################################
+
+define find-subdir-subdir-files
+$(filter-out $(patsubst %,$(1)/%,$(3)),$(patsubst ./%,%,$(shell cd \
+ $(LOCAL_PATH) ; find $(1) -maxdepth 1 -name $(2))))
+endef
+
+###########################################################
+## Find all of the files matching pattern
+## SRC_FILES := $(call all-subdir-java-files)
+###########################################################
+
+define find-subdir-assets
+$(if $(1),$(patsubst ./%,%, \
+ $(shell if [ -d $(1) ] ; then cd $(1) ; find ./ -type f -and -not -type l ; fi)), \
+ $(warning Empty argument supplied to find-subdir-assets) \
+)
+endef
+
+###########################################################
+## Find various file types in a list of directories relative to $(LOCAL_PATH)
+###########################################################
+
+define find-other-java-files
+ $(call find-subdir-files,$(1) -name "*.java" -and -not -name ".*")
+endef
+
+define find-other-html-files
+ $(call find-subdir-files,$(1) -name "*.html" -and -not -name ".*")
+endef
+
+###########################################################
+## Scan through each directory of $(1) looking for files
+## that match $(2) using $(wildcard). Useful for seeing if
+## a given directory or one of its parents contains
+## a particular file. Returns the first match found,
+## starting furthest from the root.
+###########################################################
+
+define find-parent-file
+$(strip \
+ $(eval _fpf := $(wildcard $(strip $(1))/$(strip $(2)))) \
+ $(if $(_fpf),$(_fpf), \
+ $(if $(filter-out ./ .,$(1)), \
+ $(call find-parent-file,$(patsubst %/,%,$(dir $(1))),$(2)) \
+ ) \
+ ) \
+)
+endef
+
+###########################################################
+## Function we can evaluate to introduce a dynamic dependency
+###########################################################
+
+define add-dependency
+$(1): $(2)
+endef
+
+###########################################################
+## Set up the dependencies for a prebuilt target
+## $(call add-prebuilt-file, srcfile, [targetclass])
+###########################################################
+
+define add-prebuilt-file
+ $(eval $(include-prebuilt))
+endef
+
+define include-prebuilt
+ include $$(CLEAR_VARS)
+ LOCAL_SRC_FILES := $(1)
+ LOCAL_BUILT_MODULE_STEM := $(1)
+ LOCAL_MODULE_SUFFIX := $$(suffix $(1))
+ LOCAL_MODULE := $$(basename $(1))
+ LOCAL_MODULE_CLASS := $(2)
+ include $$(BUILD_PREBUILT)
+endef
+
+###########################################################
+## do multiple prebuilts
+## $(call target class, files ...)
+###########################################################
+
+define add-prebuilt-files
+ $(foreach f,$(2),$(call add-prebuilt-file,$f,$(1)))
+endef
+
+
+
+###########################################################
+## The intermediates directory. Where object files go for
+## a given target. We could technically get away without
+## the "_intermediates" suffix on the directory, but it's
+## nice to be able to grep for that string to find out if
+## anyone's abusing the system.
+###########################################################
+
+# $(1): target class, like "APPS"
+# $(2): target name, like "NotePad"
+# $(3): if non-empty, this is a HOST target.
+# $(4): if non-empty, force the intermediates to be COMMON
+define intermediates-dir-for
+$(strip \
+ $(eval _idfClass := $(strip $(1))) \
+ $(if $(_idfClass),, \
+ $(error $(LOCAL_PATH): Class not defined in call to intermediates-dir-for)) \
+ $(eval _idfName := $(strip $(2))) \
+ $(if $(_idfName),, \
+ $(error $(LOCAL_PATH): Name not defined in call to intermediates-dir-for)) \
+ $(eval _idfPrefix := $(if $(strip $(3)),HOST,TARGET)) \
+ $(if $(filter $(_idfClass),$(COMMON_MODULE_CLASSES))$(4), \
+ $(eval _idfIntBase := $($(_idfPrefix)_OUT_COMMON_INTERMEDIATES)) \
+ , \
+ $(eval _idfIntBase := $($(_idfPrefix)_OUT_INTERMEDIATES)) \
+ ) \
+ $(_idfIntBase)/$(_idfClass)/$(_idfName)_intermediates \
+)
+endef
+
+# Uses LOCAL_MODULE_CLASS, LOCAL_MODULE, and LOCAL_IS_HOST_MODULE
+# to determine the intermediates directory.
+#
+# $(1): if non-empty, force the intermediates to be COMMON
+define local-intermediates-dir
+$(strip \
+ $(if $(strip $(LOCAL_MODULE_CLASS)),, \
+ $(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined before call to local-intermediates-dir)) \
+ $(if $(strip $(LOCAL_MODULE)),, \
+ $(error $(LOCAL_PATH): LOCAL_MODULE not defined before call to local-intermediates-dir)) \
+ $(call intermediates-dir-for,$(LOCAL_MODULE_CLASS),$(LOCAL_MODULE),$(LOCAL_IS_HOST_MODULE),$(1)) \
+)
+endef
+
+###########################################################
+## Convert "path/to/libXXX.so" to "-lXXX".
+## Any "path/to/libXXX.a" elements pass through unchanged.
+###########################################################
+
+define normalize-libraries
+$(foreach so,$(filter %.so,$(1)),-l$(patsubst lib%.so,%,$(notdir $(so))))\
+$(filter-out %.so,$(1))
+endef
+
+# TODO: change users to call the common version.
+define normalize-host-libraries
+$(call normalize-libraries,$(1))
+endef
+
+define normalize-target-libraries
+$(call normalize-libraries,$(1))
+endef
+
+###########################################################
+## Convert a list of short module names (e.g., "framework", "Browser")
+## into the list of files that are built for those modules.
+## NOTE: this won't return reliable results until after all
+## sub-makefiles have been included.
+## $(1): target list
+###########################################################
+
+define module-built-files
+$(foreach module,$(1),$(ALL_MODULES.$(module).BUILT))
+endef
+
+###########################################################
+## Convert a list of short modules names (e.g., "framework", "Browser")
+## into the list of files that are installed for those modules.
+## NOTE: this won't return reliable results until after all
+## sub-makefiles have been included.
+## $(1): target list
+###########################################################
+
+define module-installed-files
+$(foreach module,$(1),$(ALL_MODULES.$(module).INSTALLED))
+endef
+
+###########################################################
+## Convert "framework framework-res ext" to "out/.../javalib.jar ..."
+## This lets us treat framework-res as a normal library.
+## $(1): library list
+## $(2): Non-empty if IS_HOST_MODULE
+###########################################################
+
+# $(1): library name
+# $(2): Non-empty if IS_HOST_MODULE
+define _java-lib-dir
+$(call intermediates-dir-for, \
+ $(if $(filter framework-res,$(1)),APPS,JAVA_LIBRARIES),$(1),$(2))
+endef
+
+# $(1): library name
+define _java-lib-classes.jar
+$(if $(filter framework-res,$(1)),package$(COMMON_ANDROID_PACKAGE_SUFFIX),classes$(COMMON_JAVA_PACKAGE_SUFFIX))
+endef
+
+# $(1): library name
+# $(2): Non-empty if IS_HOST_MODULE
+define _java-lib-full-classes.jar
+$(call _java-lib-dir,$(1),$(2))/$(call _java-lib-classes.jar,$(1))
+endef
+
+# $(1): library name list
+# $(2): Non-empty if IS_HOST_MODULE
+define java-lib-files
+$(foreach lib,$(1),$(call _java-lib-full-classes.jar,$(lib),$(2)))
+endef
+
+# $(1): library name
+define _java-lib-dep
+$(if $(filter framework-res,$(1)),package$(COMMON_ANDROID_PACKAGE_SUFFIX),javalib$(COMMON_JAVA_PACKAGE_SUFFIX))
+endef
+
+# $(1): library name
+# $(2): Non-empty if IS_HOST_MODULE
+define _java-lib-full-dep
+$(call _java-lib-dir,$(1),$(2))/$(call _java-lib-dep,$(1))
+endef
+
+# $(1): library name list
+# $(2): Non-empty if IS_HOST_MODULE
+define java-lib-deps
+$(foreach lib,$(1),$(call _java-lib-full-dep,$(lib),$(2)))
+endef
+
+###########################################################
+## Convert "a b c" into "a:b:c"
+###########################################################
+
+empty :=
+space := $(empty) $(empty)
+
+define normalize-path-list
+$(subst $(space),:,$(strip $(1)))
+endef
+
+###########################################################
+## Convert "a=b c= d e = f" into "a=b c=d e=f"
+##
+## $(1): list to collapse
+## $(2): if set, separator word; usually "=", ":", or ":="
+## Defaults to "=" if not set.
+###########################################################
+
+define collapse-pairs
+$(eval _cpSEP := $(strip $(if $(2),$(2),=)))\
+$(subst $(space)$(_cpSEP)$(space),$(_cpSEP),$(strip \
+ $(subst $(_cpSEP), $(_cpSEP) ,$(1))))
+endef
+
+
+###########################################################
+## MODULE_TAG set operations
+###########################################################
+
+# Given a list of tags, return the targets that specify
+# any of those tags.
+# $(1): tag list
+define modules-for-tag-list
+$(sort $(foreach tag,$(1),$(ALL_MODULE_TAGS.$(tag))))
+endef
+
+# Same as modules-for-tag-list, but operates on
+# ALL_MODULE_NAME_TAGS.
+# $(1): tag list
+define module-names-for-tag-list
+$(sort $(foreach tag,$(1),$(ALL_MODULE_NAME_TAGS.$(tag))))
+endef
+
+# Given an accept and reject list, find the matching
+# set of targets. If a target has multiple tags and
+# any of them are rejected, the target is rejected.
+# Reject overrides accept.
+# $(1): list of tags to accept
+# $(2): list of tags to reject
+#TODO(dbort): do $(if $(strip $(1)),$(1),$(ALL_MODULE_TAGS))
+define get-tagged-modules
+$(filter-out \
+ $(call modules-for-tag-list,$(2)), \
+ $(call modules-for-tag-list,$(1)))
+endef
+
+
+###########################################################
+## Package filtering
+###########################################################
+
+# Given a list of installed modules (short or long names)
+# return a list of the packages (yes, .apk packages, not
+# modules in general) that are overridden by this list and,
+# therefore, should not be installed.
+# $(1): mixed list of installed modules
+# TODO: This is fragile; find a reliable way to get this information.
+define _get-package-overrides
+ $(eval ### Discard any words containing slashes, unless they end in .apk, \
+ ### in which case trim off the directory component and the suffix. \
+ ### If there are no slashes, keep the entire word.)
+ $(eval _gpo_names := $(subst /,@@@ @@@,$(1)))
+ $(eval _gpo_names := \
+ $(filter %.apk,$(_gpo_names)) \
+ $(filter-out %@@@ @@@%,$(_gpo_names)))
+ $(eval _gpo_names := $(patsubst %.apk,%,$(_gpo_names)))
+ $(eval _gpo_names := $(patsubst @@@%,%,$(_gpo_names)))
+
+ $(eval ### Remove any remaining words that contain dots.)
+ $(eval _gpo_names := $(subst .,@@@ @@@,$(_gpo_names)))
+ $(eval _gpo_names := $(filter-out %@@@ @@@%,$(_gpo_names)))
+
+ $(eval ### Now we have a list of any words that could possibly refer to \
+ ### packages, although there may be words that do not. Only \
+ ### real packages will be present under PACKAGES.*, though.)
+ $(foreach _gpo_name,$(_gpo_names),$(PACKAGES.$(_gpo_name).OVERRIDES))
+endef
+
+define get-package-overrides
+$(strip $(sort $(call _get-package-overrides,$(1))))
+endef
+
+###########################################################
+## Output the command lines, or not
+###########################################################
+
+ifeq ($(strip $(SHOW_COMMANDS)),)
+define pretty
+@echo $1
+endef
+hide := @
+else
+define pretty
+endef
+hide :=
+endif
+
+###########################################################
+## Dump the variables that are associated with targets
+###########################################################
+
+define dump-module-variables
+@echo all_dependencies=$^
+@echo PRIVATE_YACCFLAGS=$(PRIVATE_YACCFLAGS);
+@echo PRIVATE_CFLAGS=$(PRIVATE_CFLAGS);
+@echo PRIVATE_CPPFLAGS=$(PRIVATE_CPPFLAGS);
+@echo PRIVATE_DEBUG_CFLAGS=$(PRIVATE_DEBUG_CFLAGS);
+@echo PRIVATE_C_INCLUDES=$(PRIVATE_C_INCLUDES);
+@echo PRIVATE_LDFLAGS=$(PRIVATE_LDFLAGS);
+@echo PRIVATE_LDLIBS=$(PRIVATE_LDLIBS);
+@echo PRIVATE_ARFLAGS=$(PRIVATE_ARFLAGS);
+@echo PRIVATE_AAPT_FLAGS=$(PRIVATE_AAPT_FLAGS);
+@echo PRIVATE_DX_FLAGS=$(PRIVATE_DX_FLAGS);
+@echo PRIVATE_JAVA_LIBRARIES=$(PRIVATE_JAVA_LIBRARIES);
+@echo PRIVATE_ALL_SHARED_LIBRARIES=$(PRIVATE_ALL_SHARED_LIBRARIES);
+@echo PRIVATE_ALL_STATIC_LIBRARIES=$(PRIVATE_ALL_STATIC_LIBRARIES);
+@echo PRIVATE_ALL_WHOLE_STATIC_LIBRARIES=$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES);
+@echo PRIVATE_ALL_OBJECTS=$(PRIVATE_ALL_OBJECTS);
+endef
+
+###########################################################
+## Commands for using sed to replace given variable values
+###########################################################
+
+define transform-variables
+@mkdir -p $(dir $@)
+@echo "Sed: $(if $(PRIVATE_MODULE),$(PRIVATE_MODULE),$@) <= $<"
+$(hide) sed $(foreach var,$(REPLACE_VARS),-e "s/{{$(var)}}/$(subst /,\/,$(PWD)/$($(var)))/g") $< >$@
+$(hide) if [ "$(suffix $@)" = ".sh" ]; then chmod a+rx $@; fi
+endef
+
+
+###########################################################
+## Commands for munging the dependency files GCC generates
+###########################################################
+
+define transform-d-to-p
+@cp $(@:%.o=%.d) $(@:%.o=%.P); \
+ sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
+ -e '/^$$/ d' -e 's/$$/ :/' < $(@:%.o=%.d) >> $(@:%.o=%.P); \
+ rm -f $(@:%.o=%.d)
+endef
+
+###########################################################
+## Commands for running lex
+###########################################################
+
+define transform-l-to-cpp
+@mkdir -p $(dir $@)
+@echo "Lex: $(PRIVATE_MODULE) <= $<"
+$(hide) $(LEX) -o$@ $<
+endef
+
+###########################################################
+## Commands for running yacc
+##
+## Because the extension of c++ files can change, the
+## extension must be specified in $1.
+## E.g, "$(call transform-y-to-cpp,.cpp)"
+###########################################################
+
+define transform-y-to-cpp
+@mkdir -p $(dir $@)
+@echo "Yacc: $(PRIVATE_MODULE) <= $<"
+$(YACC) $(PRIVATE_YACCFLAGS) -o $@ $<
+touch $(@:$1=$(YACC_HEADER_SUFFIX))
+echo '#ifndef '$(@F:$1=_h) > $(@:$1=.h)
+echo '#define '$(@F:$1=_h) >> $(@:$1=.h)
+cat $(@:$1=$(YACC_HEADER_SUFFIX)) >> $(@:$1=.h)
+echo '#endif' >> $(@:$1=.h)
+rm -f $(@:$1=$(YACC_HEADER_SUFFIX))
+endef
+
+
+###########################################################
+## Commands for running aidl
+###########################################################
+
+define transform-aidl-to-java
+@mkdir -p $(dir $@)
+@echo "Aidl: $(PRIVATE_MODULE) <= $<"
+$(hide) $(AIDL) -d$(patsubst %.java,%.P,$@) $(PRIVATE_AIDL_FLAGS) $< $@
+endef
+#$(AIDL) $(PRIVATE_AIDL_FLAGS) $< - | indent -nut -br -npcs -l1000 > $@
+
+
+
+###########################################################
+## Commands for running gcc to compile a C++ file
+###########################################################
+
+define transform-cpp-to-o
+@mkdir -p $(dir $@)
+@echo "target $(PRIVATE_ARM_MODE) C++: $(PRIVATE_MODULE) <= $<"
+$(hide) $(PRIVATE_CXX) \
+ $(foreach incdir, \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(TARGET_PROJECT_INCLUDES) \
+ $(TARGET_C_INCLUDES) \
+ ) \
+ $(PRIVATE_C_INCLUDES) \
+ , \
+ -I $(incdir) \
+ ) \
+ -c \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(TARGET_GLOBAL_CFLAGS) \
+ $(TARGET_GLOBAL_CPPFLAGS) \
+ $(PRIVATE_ARM_CFLAGS) \
+ ) \
+ -fno-rtti \
+ $(PRIVATE_CFLAGS) \
+ $(PRIVATE_CPPFLAGS) \
+ $(PRIVATE_DEBUG_CFLAGS) \
+ -MD -o $@ $<
+$(hide) $(transform-d-to-p)
+endef
+
+
+###########################################################
+## Commands for running gcc to compile a C file
+###########################################################
+
+# $(1): extra flags
+define transform-c-or-s-to-o-no-deps
+@mkdir -p $(dir $@)
+$(hide) $(PRIVATE_CC) \
+ $(foreach incdir, \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(TARGET_PROJECT_INCLUDES) \
+ $(TARGET_C_INCLUDES) \
+ ) \
+ $(PRIVATE_C_INCLUDES) \
+ , \
+ -I $(incdir) \
+ ) \
+ -c \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(TARGET_GLOBAL_CFLAGS) \
+ $(PRIVATE_ARM_CFLAGS) \
+ ) \
+ $(PRIVATE_CFLAGS) \
+ $(1) \
+ $(PRIVATE_DEBUG_CFLAGS) \
+ -MD -o $@ $<
+endef
+
+define transform-c-to-o-no-deps
+@echo "target $(PRIVATE_ARM_MODE) C: $(PRIVATE_MODULE) <= $<"
+$(call transform-c-or-s-to-o-no-deps, )
+endef
+
+define transform-s-to-o-no-deps
+@echo "target asm: $(PRIVATE_MODULE) <= $<"
+$(call transform-c-or-s-to-o-no-deps, $(PRIVATE_ASFLAGS))
+endef
+
+define transform-c-to-o
+$(transform-c-to-o-no-deps)
+$(hide) $(transform-d-to-p)
+endef
+
+define transform-s-to-o
+$(transform-s-to-o-no-deps)
+$(hide) $(transform-d-to-p)
+endef
+
+###########################################################
+## Commands for running gcc to compile a host C++ file
+###########################################################
+
+define transform-host-cpp-to-o
+@mkdir -p $(dir $@)
+@echo "host C++: $(PRIVATE_MODULE) <= $<"
+$(hide) $(PRIVATE_CXX) \
+ $(foreach incdir, \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_PROJECT_INCLUDES) \
+ $(HOST_C_INCLUDES) \
+ ) \
+ $(PRIVATE_C_INCLUDES) \
+ , \
+ -I $(incdir) \
+ ) \
+ -c \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_GLOBAL_CFLAGS) \
+ $(HOST_GLOBAL_CPPFLAGS) \
+ ) \
+ $(PRIVATE_CFLAGS) \
+ $(PRIVATE_CPPFLAGS) \
+ $(PRIVATE_DEBUG_CFLAGS) \
+ -MD -o $@ $<
+$(transform-d-to-p)
+endef
+
+
+###########################################################
+## Commands for running gcc to compile a host C file
+###########################################################
+
+# $(1): extra flags
+define transform-host-c-or-s-to-o-no-deps
+@mkdir -p $(dir $@)
+$(hide) $(PRIVATE_CC) \
+ $(foreach incdir, \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_PROJECT_INCLUDES) \
+ $(HOST_C_INCLUDES) \
+ ) \
+ $(PRIVATE_C_INCLUDES) \
+ , \
+ -I $(incdir) \
+ ) \
+ -c \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_GLOBAL_CFLAGS) \
+ ) \
+ $(PRIVATE_CFLAGS) \
+ $(1) \
+ $(PRIVATE_DEBUG_CFLAGS) \
+ -MD -o $@ $<
+endef
+
+define transform-host-c-to-o-no-deps
+@echo "host C: $(PRIVATE_MODULE) <= $<"
+$(call transform-host-c-or-s-to-o-no-deps, )
+endef
+
+define transform-host-s-to-o-no-deps
+@echo "host asm: $(PRIVATE_MODULE) <= $<"
+$(call transform-host-c-or-s-to-o-no-deps, $(PRIVATE_ASFLAGS))
+endef
+
+define transform-host-c-to-o
+$(transform-host-c-to-o-no-deps)
+$(transform-d-to-p)
+endef
+
+define transform-host-s-to-o
+$(transform-host-s-to-o-no-deps)
+$(transform-d-to-p)
+endef
+
+###########################################################
+## Commands for running ar
+###########################################################
+
+# Explicitly delete the archive first so that ar doesn't
+# try to add to an existing archive.
+define transform-o-to-static-lib
+@mkdir -p $(dir $@)
+@echo "target StaticLib: $(PRIVATE_MODULE) ($@)"
+@rm -f $@
+$(hide) $(TARGET_AR) $(TARGET_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $^
+endef
+
+###########################################################
+## Commands for running host ar
+###########################################################
+
+# Explicitly delete the archive first so that ar doesn't
+# try to add to an existing archive.
+define transform-host-o-to-static-lib
+@mkdir -p $(dir $@)
+@echo "host StaticLib: $(PRIVATE_MODULE) ($@)"
+@rm -f $@
+$(HOST_AR) $(HOST_GLOBAL_ARFLAGS) $(PRIVATE_ARFLAGS) $@ $^
+endef
+
+
+###########################################################
+## Commands for running gcc to link a shared library or package
+###########################################################
+
+# ld just seems to be so finicky with command order that we allow
+# it to be overriden en-masse see combo/linux-arm.make for an example.
+ifneq ($(HOST_CUSTOM_LD_COMMAND),true)
+define transform-host-o-to-shared-lib-inner
+$(HOST_CXX) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ -Wl,-rpath,\$$ORIGIN/../lib \
+ -shared -Wl,-soname,$(notdir $@) \
+ $(PRIVATE_LDFLAGS) \
+ $(HOST_GLOBAL_LD_DIRS) \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_GLOBAL_LDFLAGS) \
+ ) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDLIBS)
+endef
+endif
+
+define transform-host-o-to-shared-lib
+@mkdir -p $(dir $@)
+@echo "host SharedLib: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-host-o-to-shared-lib-inner)
+endef
+
+define transform-host-o-to-package
+@mkdir -p $(dir $@)
+@echo "host Package: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-host-o-to-shared-lib-inner)
+endef
+
+
+###########################################################
+## Commands for running gcc to link a shared library or package
+###########################################################
+
+#echo >$@.vers "{"; \
+#echo >>$@.vers " global:"; \
+#$(BUILD_SYSTEM)/filter_symbols.sh $(TARGET_NM) " " ";" $(filter %.o,$^) | sort -u >>$@.vers; \
+#echo >>$@.vers " local:"; \
+#echo >>$@.vers " *;"; \
+#echo >>$@.vers "};"; \
+
+# -Wl,--version-script=$@.vers \
+
+# ld just seems to be so finicky with command order that we allow
+# it to be overriden en-masse see combo/linux-arm.make for an example.
+ifneq ($(TARGET_CUSTOM_LD_COMMAND),true)
+define transform-o-to-shared-lib-inner
+$(TARGET_CXX) \
+ $(TARGET_GLOBAL_LDFLAGS) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ -Wl,-rpath,\$$ORIGIN/../lib \
+ -shared -Wl,-soname,$(notdir $@) \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDLIBS)
+endef
+endif
+
+define transform-o-to-shared-lib
+@mkdir -p $(dir $@)
+@echo "target SharedLib: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-o-to-shared-lib-inner)
+endef
+
+define transform-o-to-package
+@mkdir -p $(dir $@)
+@echo "target Package: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-o-to-shared-lib-inner)
+endef
+
+
+###########################################################
+## Commands for filtering a target executable or library
+###########################################################
+
+# Because of bug 743462 ("Prelinked image magic gets stripped
+# by arm-elf-objcopy"), we have to use soslim to strip target
+# binaries.
+define transform-to-stripped
+@mkdir -p $(dir $@)
+@echo "target Strip: $(PRIVATE_MODULE) ($@)"
+$(hide) $(SOSLIM) --strip --shady --quiet $< --outfile $@
+endef
+
+define transform-to-prelinked
+@mkdir -p $(dir $@)
+@echo "target Prelink: $(PRIVATE_MODULE) ($@)"
+$(hide) $(APRIORI) \
+ --prelinkmap $(TARGET_PRELINKER_MAP) \
+ --locals-only \
+ --quiet \
+ $< \
+ --output $@
+endef
+
+
+###########################################################
+## Commands for running gcc to link an executable
+###########################################################
+
+ifneq ($(TARGET_CUSTOM_LD_COMMAND),true)
+define transform-o-to-executable-inner
+$(TARGET_CXX) \
+ $(TARGET_GLOBAL_LDFLAGS) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ -Wl,-rpath,\$$ORIGIN/../lib \
+ $(PRIVATE_LDFLAGS) \
+ $(TARGET_GLOBAL_LD_DIRS) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-target-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDLIBS)
+endef
+endif
+
+define transform-o-to-executable
+@mkdir -p $(dir $@)
+@echo "target Executable: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-o-to-executable-inner)
+endef
+
+
+###########################################################
+## Commands for running gcc to link a statically linked
+## executable. In practice, we only use this on arm, so
+## the other platforms don't have the
+## transform-o-to-static-executable defined
+###########################################################
+
+ifneq ($(TARGET_CUSTOM_LD_COMMAND),true)
+define transform-o-to-static-executable-inner
+endef
+endif
+
+define transform-o-to-static-executable
+@mkdir -p $(dir $@)
+@echo "target StaticExecutable: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-o-to-static-executable-inner)
+endef
+
+
+###########################################################
+## Commands for running gcc to link a host executable
+###########################################################
+
+ifneq ($(HOST_CUSTOM_LD_COMMAND),true)
+define transform-host-o-to-executable-inner
+$(HOST_CXX) \
+ -Wl,-rpath-link=$(TARGET_OUT_INTERMEDIATE_LIBRARIES) \
+ -Wl,-rpath,\$$ORIGIN/../lib \
+ $(HOST_GLOBAL_LD_DIRS) \
+ $(PRIVATE_LDFLAGS) \
+ $(if $(PRIVATE_NO_DEFAULT_COMPILER_FLAGS),, \
+ $(HOST_GLOBAL_LDFLAGS) \
+ ) \
+ $(PRIVATE_ALL_OBJECTS) \
+ -Wl,--whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_WHOLE_STATIC_LIBRARIES)) \
+ -Wl,--no-whole-archive \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_STATIC_LIBRARIES)) \
+ $(call normalize-host-libraries,$(PRIVATE_ALL_SHARED_LIBRARIES)) \
+ -o $@ \
+ $(PRIVATE_LDLIBS)
+endef
+endif
+
+define transform-host-o-to-executable
+@mkdir -p $(dir $@)
+@echo "host Executable: $(PRIVATE_MODULE) ($@)"
+$(hide) $(transform-host-o-to-executable-inner)
+endef
+
+
+###########################################################
+## Commands for running javac to make .class files
+###########################################################
+
+#@echo "Source intermediates dir: $(PRIVATE_SOURCE_INTERMEDIATES_DIR)"
+#@echo "Source intermediates: $$(find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java')"
+
+# TODO: Right now we generate the asset resources twice, first as part
+# of generating the Java classes, then at the end when packaging the final
+# assets. This should be changed to do one of two things: (1) Don't generate
+# any resource files the first time, only create classes during that stage;
+# or (2) Don't use the -c flag with the second stage, instead taking the
+# resource files from the first stage as additional input. My original intent
+# was to use approach (2), but this requires a little more work in the tool.
+# Maybe we should just use approach (1).
+
+# This rule creates the R.java and Manifest.java files, both of which
+# are PRODUCT-neutral. Don't pass PRODUCT_AAPT_CONFIG to this invocation.
+define create-resource-java-files
+@mkdir -p $(PRIVATE_SOURCE_INTERMEDIATES_DIR)
+@mkdir -p $(dir $(PRIVATE_RESOURCE_PUBLICS_OUTPUT))
+$(hide) $(AAPT) package $(PRIVATE_AAPT_FLAGS) -m -z \
+ $(eval # PRODUCT_AAPT_CONFIG is intentionally missing-- see comment.) \
+ $(addprefix -J , $(PRIVATE_SOURCE_INTERMEDIATES_DIR)) \
+ $(addprefix -M , $(PRIVATE_ANDROID_MANIFEST)) \
+ $(addprefix -P , $(PRIVATE_RESOURCE_PUBLICS_OUTPUT)) \
+ $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
+ $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
+ $(addprefix -I , $(PRIVATE_AAPT_INCLUDES))
+endef
+
+ifeq ($(HOST_OS),windows)
+xlint_unchecked :=
+else
+#xlint_unchecked := -Xlint:unchecked
+endif
+
+# emit-line, <word list>, <output file>
+define emit-line
+ $(if $(1),echo -n '$(strip $(1)) ' >> $(2))
+endef
+
+# dump-words-to-file, <word list>, <output file>
+define dump-words-to-file
+ @rm -f $(2)
+ @$(call emit-line,$(wordlist 1,200,$(1)),$(2))
+ @$(call emit-line,$(wordlist 201,400,$(1)),$(2))
+ @$(call emit-line,$(wordlist 401,600,$(1)),$(2))
+ @$(call emit-line,$(wordlist 601,800,$(1)),$(2))
+ @$(call emit-line,$(wordlist 801,1000,$(1)),$(2))
+ @$(call emit-line,$(wordlist 1001,1200,$(1)),$(2))
+ @$(call emit-line,$(wordlist 1201,1400,$(1)),$(2))
+ @$(call emit-line,$(wordlist 1401,1600,$(1)),$(2))
+ @$(call emit-line,$(wordlist 1601,1800,$(1)),$(2))
+ @$(call emit-line,$(wordlist 1801,2000,$(1)),$(2))
+ @$(call emit-line,$(wordlist 2001,2200,$(1)),$(2))
+ @$(call emit-line,$(wordlist 2201,2400,$(1)),$(2))
+ @$(call emit-line,$(wordlist 2401,2600,$(1)),$(2))
+ @$(call emit-line,$(wordlist 2601,2800,$(1)),$(2))
+ @$(call emit-line,$(wordlist 2801,3000,$(1)),$(2))
+ @$(call emit-line,$(wordlist 3001,3200,$(1)),$(2))
+ @$(call emit-line,$(wordlist 3201,3400,$(1)),$(2))
+ @$(call emit-line,$(wordlist 3401,3600,$(1)),$(2))
+ @$(call emit-line,$(wordlist 3601,3800,$(1)),$(2))
+ @$(call emit-line,$(wordlist 3801,4000,$(1)),$(2))
+ @$(if $(wordlist 4001,4002,$(1)),$(error Too many words ($(words $(1)))))
+endef
+
+# For a list of jar files, unzip them to a specified directory,
+# but make sure that no META-INF files come along for the ride.
+#
+# $(1): files to unzip
+# $(2): destination directory
+define unzip-jar-files
+ $(hide) for f in $(1); \
+ do \
+ if [ ! -f $$f ]; then \
+ echo Missing file $$f; \
+ exit 1; \
+ fi; \
+ unzip -q $$f -d $(2); \
+ (cd $(2) && rm -rf META-INF); \
+ done
+endef
+
+# below we write the list of java files to java-source-list to avoid argument list length problems with Cygwin
+# we filter out duplicate java file names because eclipse's compiler doesn't like them.
+define transform-java-to-classes.jar
+@echo "target Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
+@rm -f $@
+@rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+@mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+$(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES), \
+ $(PRIVATE_CLASS_INTERMEDIATES_DIR))
+$(call dump-words-to-file,$(PRIVATE_JAVA_SOURCES),$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list)
+@if [ -d "$(PRIVATE_SOURCE_INTERMEDIATES_DIR)" ]; then \
+ find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list; \
+fi
+$(hide) tr ' ' '\n' < $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list \
+ | sort -u > $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
+$(hide) $(TARGET_JAVAC) -encoding ascii $(PRIVATE_BOOTCLASSPATH) \
+ $(addprefix -classpath ,$(strip \
+ $(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
+ $(strip $(PRIVATE_JAVAC_DEBUG_FLAGS)) $(xlint_unchecked) \
+ -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR) \
+ \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq \
+ || ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
+@ rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list
+@ rm -f $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq
+@mkdir -p $(dir $@)
+$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
+ $@ $(PRIVATE_JAR_MANIFEST) -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
+@rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+endef
+
+define transform-classes.jar-to-emma
+$(hide) java -classpath $(EMMA_JAR) emma instr -outmode fullcopy -outfile \
+ $(PRIVATE_EMMA_COVERAGE_FILE) -ip $< -d $(PRIVATE_EMMA_INTERMEDIATES_DIR)
+endef
+
+#TODO: use a smaller -Xmx value for most libraries;
+# only core.jar and framework.jar need a heap this big.
+define transform-classes.jar-to-dex
+@echo "target Dex: $(PRIVATE_MODULE)"
+@mkdir -p $(dir $@)
+$(hide) $(DX) -JXms16M \
+ -JXmx1536M \
+ --dex --output=$@ \
+ $(if $(NO_OPTIMIZE_DX), \
+ --no-optimize) \
+ $(if $(GENERATE_DEX_DEBUG), \
+ --debug --verbose \
+ --dump-to=$(@:.dex=.lst) \
+ --dump-width=1000) \
+ $(PRIVATE_DX_FLAGS) \
+ $<
+endef
+
+# Create a mostly-empty .jar file that we'll add to later.
+# The MacOS jar tool doesn't like creating empty jar files,
+# so we need to give it something.
+define create-empty-package
+@mkdir -p $(dir $@)
+$(hide) touch $(dir $@)/dummy
+$(hide) (cd $(dir $@) && jar cf $(notdir $@) dummy)
+$(hide) zip -qd $@ dummy
+$(hide) rm $(dir $@)/dummy
+endef
+
+#TODO: we kinda want to build different asset packages for
+# different configurations, then combine them later (or something).
+# Per-locale, etc.
+# A list of dynamic and static parameters; build layers for
+# dynamic params that lay over the static ones.
+#TODO: update the manifest to point to the package file
+define add-assets-to-package
+$(hide) $(AAPT) package -z -u $(PRIVATE_AAPT_FLAGS) \
+ $(addprefix -c , $(PRODUCT_AAPT_CONFIG)) \
+ $(addprefix -M , $(PRIVATE_ANDROID_MANIFEST)) \
+ $(addprefix -S , $(PRIVATE_RESOURCE_DIR)) \
+ $(addprefix -A , $(PRIVATE_ASSET_DIR)) \
+ $(addprefix -I , $(PRIVATE_AAPT_INCLUDES)) \
+ -F $@
+endef
+
+#TODO: Allow library directory to be specified based on the target
+# CPU and ABI instead of being hard coded as armeabi.
+define add-jni-shared-libs-to-package
+$(hide) rm -rf $(dir $@)lib
+$(hide) mkdir -p $(dir $@)lib/armeabi
+$(hide) cp $(PRIVATE_JNI_SHARED_LIBRARIES) $(dir $@)lib/armeabi
+$(hide) (cd $(dir $@) && zip -r $(notdir $@) lib)
+$(hide) rm -rf $(dir $@)lib
+endef
+
+#TODO: use aapt instead of zip, once it supports junking the path
+# (so adding "xxx/yyy/classes.dex" appears as "classes.dex")
+#TODO: update the manifest to point to the dex file
+define add-dex-to-package
+$(hide) zip -qj $@ $(PRIVATE_DEX_FILE)
+endef
+
+define add-java-resources-to-package
+$(hide) jar uf $@ $(PRIVATE_EXTRA_JAR_ARGS)
+endef
+
+# Sign a package using the specified key/cert.
+#
+define sign-package
+$(hide) mv $@ $@.unsigned
+$(hide) java -jar $(SIGNAPK_JAR) \
+ $(PRIVATE_CERTIFICATE) $(PRIVATE_PRIVATE_KEY) $@.unsigned $@.signed
+$(hide) mv $@.signed $@
+endef
+
+# Align STORED entries of a package on 4-byte boundaries
+# to make them easier to mmap.
+#
+define align-package
+$(hide) mv $@ $@.unaligned
+$(hide) $(ZIPALIGN) -f 4 $@.unaligned $@.aligned
+$(hide) mv $@.aligned $@
+endef
+
+define install-dex-debug
+$(hide) if [ -f "$(PRIVATE_INTERMEDIATES_DIR)/classes.dex" ]; then \
+ mkdir -p $(TOP)/dalvik/DEBUG-FILES; \
+ $(ACP) $(PRIVATE_INTERMEDIATES_DIR)/classes.dex \
+ $(TOP)/dalvik/DEBUG-FILES/$(PRIVATE_MODULE).dex; \
+ fi
+$(hide) if [ -f "$(PRIVATE_INTERMEDIATES_DIR)/classes.lst" ]; then \
+ mkdir -p $(TOP)/dalvik/DEBUG-FILES; \
+ $(ACP) $(PRIVATE_INTERMEDIATES_DIR)/classes.lst \
+ $(TOP)/dalvik/DEBUG-FILES/$(PRIVATE_MODULE).lst; \
+ fi
+endef
+
+# TODO(joeo): If we can ever upgrade to post 3.81 make and get the
+# new prebuilt rules to work, we should change this to copy the
+# resources to the out directory and then copy the resources.
+
+# Note: not using aapt tool for this because we aren't making
+# an android package for the host.
+define transform-host-java-to-package
+@echo "host Java: $(PRIVATE_MODULE) ($(PRIVATE_CLASS_INTERMEDIATES_DIR))"
+@rm -f $@
+@rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+@mkdir -p $(dir $@)
+@mkdir -p $(PRIVATE_CLASS_INTERMEDIATES_DIR)
+$(call unzip-jar-files,$(PRIVATE_STATIC_JAVA_LIBRARIES), \
+ $(PRIVATE_CLASS_INTERMEDIATES_DIR))
+$(call dump-words-to-file,$(sort\
+ $(PRIVATE_JAVA_SOURCES)),\
+ $(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq)
+$(hide) $(HOST_JAVAC) -encoding ascii -g \
+ $(xlint_unchecked) \
+ $(addprefix -classpath ,$(strip \
+ $(call normalize-path-list,$(PRIVATE_ALL_JAVA_LIBRARIES)))) \
+ -extdirs "" -d $(PRIVATE_CLASS_INTERMEDIATES_DIR)\
+ \@$(PRIVATE_CLASS_INTERMEDIATES_DIR)/java-source-list-uniq || \
+ ( rm -rf $(PRIVATE_CLASS_INTERMEDIATES_DIR) ; exit 41 )
+$(hide) jar $(if $(strip $(PRIVATE_JAR_MANIFEST)),-cfm,-cf) \
+ $@ $(PRIVATE_JAR_MANIFEST) $(PRIVATE_EXTRA_JAR_ARGS) \
+ -C $(PRIVATE_CLASS_INTERMEDIATES_DIR) .
+endef
+
+###########################################################
+## Obfuscate a jar file
+###########################################################
+
+# PRIVATE_KEEP_FILE is a file containing a list of classes
+# PRIVATE_INTERMEDIATES_DIR is a directory we can use for temporary files
+# The module using this must depend on
+# $(HOST_OUT_JAVA_LIBRARIES)/proguard-4.0.1.jar
+define obfuscate-jar
+@echo "Obfuscate jar: $(notdir $@) ($@)"
+@mkdir -p $(dir $@)
+@rm -f $@
+@mkdir -p $(PRIVATE_INTERMEDIATES_DIR)
+$(hide) sed -e 's/^/-keep class /' < $(PRIVATE_KEEP_FILE) > \
+ $(PRIVATE_INTERMEDIATES_DIR)/keep.pro
+$(hide) java -Xmx512M -jar $(HOST_OUT_JAVA_LIBRARIES)/proguard-4.0.1.jar \
+ -injars $< \
+ -outjars $@ \
+ -target 1.5 \
+ -dontnote -dontwarn \
+ -printmapping $(PRIVATE_INTERMEDIATES_DIR)/out.map \
+ -forceprocessing \
+ -renamesourcefileattribute SourceFile \
+ -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod \
+ -repackageclasses \
+ -keepclassmembers "class * { public protected *; }" \
+ @$(PRIVATE_INTERMEDIATES_DIR)/keep.pro
+endef
+
+###########################################################
+## Commands for copying files
+###########################################################
+
+# Define a rule to copy a header. Used via $(eval) by copy_headers.make.
+# $(1): source header
+# $(2): destination header
+define copy-one-header
+$(2): $(1)
+ @echo "Header: $$@"
+ $$(copy-file-to-new-target-with-cp)
+endef
+
+# Define a rule to copy a file. For use via $(eval).
+# $(1): source file
+# $(2): destination file
+define copy-one-file
+$(2): $(1) | $(ACP)
+ @echo "Copy: $$@"
+ $$(copy-file-to-target)
+endef
+
+# The -t option to acp and the -p option to cp is
+# required for OSX. OSX has a ridiculous restriction
+# where it's an error for a .a file's modification time
+# to disagree with an internal timestamp, and this
+# macro is used to install .a files (among other things).
+
+# Copy a single file from one place to another,
+# preserving permissions and overwriting any existing
+# file.
+define copy-file-to-target
+@mkdir -p $(dir $@)
+$(hide) $(ACP) -fpt $< $@
+endef
+
+# The same as copy-file-to-target, but use the local
+# cp command instead of acp.
+define copy-file-to-target-with-cp
+@mkdir -p $(dir $@)
+$(hide) cp -fp $< $@
+endef
+
+# The same as copy-file-to-target, but don't preserve
+# the old modification time.
+define copy-file-to-new-target
+@mkdir -p $(dir $@)
+$(hide) $(ACP) -fp $< $@
+endef
+
+# The same as copy-file-to-new-target, but use the local
+# cp command instead of acp.
+define copy-file-to-new-target-with-cp
+@mkdir -p $(dir $@)
+$(hide) cp -f $< $@
+endef
+
+# Copy a prebuilt file to a target location.
+define transform-prebuilt-to-target
+@echo "$(if $(PRIVATE_IS_HOST_MODULE),host,target) Prebuilt: $(PRIVATE_MODULE) ($@)"
+$(copy-file-to-target)
+endef
+
+
+###########################################################
+## On some platforms (MacOS), after copying a static
+## library, ranlib must be run to update an internal
+## timestamp!?!?!
+###########################################################
+
+ifeq ($(HOST_RUN_RANLIB_AFTER_COPYING),true)
+define transform-host-ranlib-copy-hack
+ $(hide) ranlib $@ || true
+endef
+else
+define transform-host-ranlib-copy-hack
+true
+endef
+endif
+
+ifeq ($(TARGET_RUN_RANLIB_AFTER_COPYING),true)
+define transform-ranlib-copy-hack
+ $(hide) ranlib $@
+endef
+else
+define transform-ranlib-copy-hack
+true
+endef
+endif
+
+
+###########################################################
+## Stuff source generated from one-off tools
+###########################################################
+
+define transform-generated-source
+@echo "target Generated: $(PRIVATE_MODULE) <= $<"
+@mkdir -p $(dir $@)
+$(hide) $(PRIVATE_CUSTOM_TOOL)
+endef
+
+
+###########################################################
+## Assertions about attributes of the target
+###########################################################
+
+# $(1): The file to check
+ifndef get-file-size
+$(error HOST_OS must define get-file-size)
+endif
+
+# $(1): The file to check (often $@)
+# $(2): The maximum size, in decimal bytes
+#
+# If $(2) is empty, evaluates to "true"
+#
+# Reserve bad blocks. Make sure that MAX(1% of partition size, 2 blocks)
+# is left over after the image has been flashed. Round the 1% up to the
+# next whole flash block size.
+define assert-max-file-size
+$(if $(2), \
+ fileSize=`$(call get-file-size,$(1))`; \
+ maxSize=$(2); \
+ onePct=`expr "(" $$maxSize + 99 ")" / 100`; \
+ onePct=`expr "(" "(" $$onePct + $(BOARD_FLASH_BLOCK_SIZE) - 1 ")" / \
+ $(BOARD_FLASH_BLOCK_SIZE) ")" "*" $(BOARD_FLASH_BLOCK_SIZE)`; \
+ reserve=`expr 2 "*" $(BOARD_FLASH_BLOCK_SIZE)`; \
+ if [ "$$onePct" -gt "$$reserve" ]; then \
+ reserve="$$onePct"; \
+ fi; \
+ maxSize=`expr $$maxSize - $$reserve`; \
+ if [ "$$fileSize" -gt "$$maxSize" ]; then \
+ echo "error: $(1) too large ($$fileSize > [$(2) - $$reserve])"; \
+ false; \
+ fi \
+ , \
+ true \
+ )
+endef
+
+###########################################################
+## Other includes
+###########################################################
+
+# -----------------------------------------------------------------
+# Rules and functions to help copy important files to DIST_DIR
+# when requested.
+include $(BUILD_SYSTEM)/distdir.mk
+
+
+# broken:
+# $(foreach file,$^,$(if $(findstring,.a,$(suffix $file)),-l$(file),$(file)))
+
+###########################################################
+## Misc notes
+###########################################################
+
+#DEPDIR = .deps
+#df = $(DEPDIR)/$(*F)
+
+#SRCS = foo.c bar.c ...
+
+#%.o : %.c
+# @$(MAKEDEPEND); \
+# cp $(df).d $(df).P; \
+# sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
+# -e '/^$$/ d' -e 's/$$/ :/' < $(df).d >> $(df).P; \
+# rm -f $(df).d
+# $(COMPILE.c) -o $@ $<
+
+#-include $(SRCS:%.c=$(DEPDIR)/%.P)
+
+
+#%.o : %.c
+# $(COMPILE.c) -MD -o $@ $<
+# @cp $*.d $*.P; \
+# sed -e 's/#.*//' -e 's/^[^:]*: *//' -e 's/ *\\$$//' \
+# -e '/^$$/ d' -e 's/$$/ :/' < $*.d >> $*.P; \
+# rm -f $*.d
diff --git a/core/device.mk b/core/device.mk
new file mode 100644
index 000000000..20ff447ca
--- /dev/null
+++ b/core/device.mk
@@ -0,0 +1,76 @@
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+_device_var_list := \
+ DEVICE_NAME \
+ DEVICE_BOARD \
+ DEVICE_REGION
+
+define dump-device
+$(info ==== $(1) ====)\
+$(foreach v,$(_device_var_list),\
+$(info DEVICES.$(1).$(v) := $(DEVICES.$(1).$(v))))\
+$(info --------)
+endef
+
+define dump-devices
+$(foreach p,$(DEVICES),$(call dump-device,$(p)))
+endef
+
+#
+# $(1): device to inherit
+#
+define inherit-device
+ $(foreach v,$(_device_var_list), \
+ $(eval $(v) := $($(v)) $(INHERIT_TAG)$(strip $(1))))
+endef
+
+#
+# $(1): device makefile list
+#
+#TODO: check to make sure that devices have all the necessary vars defined
+define import-devices
+$(call import-nodes,DEVICES,$(1),$(_device_var_list))
+endef
+
+
+#
+# $(1): short device name like "sooner"
+#
+define _resolve-short-device-name
+ $(eval dn := $(strip $(1)))
+ $(eval d := \
+ $(foreach d,$(DEVICES), \
+ $(if $(filter $(dn),$(DEVICES.$(d).DEVICE_NAME)), \
+ $(d) \
+ )) \
+ )
+ $(eval d := $(sort $(d)))
+ $(if $(filter 1,$(words $(d))), \
+ $(d), \
+ $(if $(filter 0,$(words $(d))), \
+ $(error No matches for device "$(dn)"), \
+ $(error Device "$(dn)" ambiguous: matches $(d)) \
+ ) \
+ )
+endef
+
+#
+# $(1): short device name like "sooner"
+#
+define resolve-short-device-name
+$(strip $(call _resolve-short-device-name,$(1)))
+endef
diff --git a/core/distdir.mk b/core/distdir.mk
new file mode 100644
index 000000000..e04938bf7
--- /dev/null
+++ b/core/distdir.mk
@@ -0,0 +1,75 @@
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# When specifying "dist", the user has asked that we copy the important
+# files from this build into DIST_DIR.
+
+.PHONY: dist
+dist: ;
+
+dist_goal := $(strip $(filter dist,$(MAKECMDGOALS)))
+MAKECMDGOALS := $(strip $(filter-out dist,$(MAKECMDGOALS)))
+ifeq (,$(strip $(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS))))
+# The commandline was something like "make dist" or "make dist showcommands".
+# Add a dependency on a real target.
+dist: $(DEFAULT_GOAL)
+endif
+
+ifdef dist_goal
+
+# $(1): source file
+# $(2): destination file
+# $(3): goals that should copy the file
+#
+define copy-one-dist-file
+$(3): $(2)
+$(2): $(1)
+ @echo "Dist: $$@"
+ $$(copy-file-to-new-target-with-cp)
+endef
+
+# Other parts of the system should use this function to associate
+# certain files with certain goals. When those goals are built
+# and "dist" is specified, the marked files will be copied to DIST_DIR.
+#
+# $(1): a list of goals (e.g. droid, sdk, pdk, ndk)
+# $(2): the dist files to add to those goals. If the file contains ':',
+# the text following the colon is the name that the file is copied
+# to under the dist directory. Subdirs are ok, and will be created
+# at copy time if necessary.
+define dist-for-goals
+$(foreach file,$(2), \
+ $(eval fw := $(subst :,$(space),$(file))) \
+ $(eval src := $(word 1,$(fw))) \
+ $(eval dst := $(word 2,$(fw))) \
+ $(eval dst := $(if $(dst),$(dst),$(notdir $(src)))) \
+ $(eval \
+ $(call copy-one-dist-file, \
+ $(src), \
+ $(DIST_DIR)/$(dst), \
+ $(1) \
+ ) \
+ ) \
+ )
+endef
+
+else # !dist_goal
+
+# empty definition when not building dist
+define dist-for-goals
+endef
+
+endif # !dist_goal
diff --git a/core/droiddoc.mk b/core/droiddoc.mk
new file mode 100644
index 000000000..a279c8282
--- /dev/null
+++ b/core/droiddoc.mk
@@ -0,0 +1,158 @@
+###########################################################
+## Standard rules for building documentation
+###########################################################
+
+LOCAL_IS_HOST_MODULE := $(strip $(LOCAL_IS_HOST_MODULE))
+ifdef LOCAL_IS_HOST_MODULE
+my_prefix:=HOST_
+else
+my_prefix:=TARGET_
+endif
+
+LOCAL_MODULE_CLASS := $(strip $(LOCAL_MODULE_CLASS))
+ifndef LOCAL_MODULE_CLASS
+$(error $(LOCAL_PATH): LOCAL_MODULE_CLASS not defined)
+endif
+
+full_src_files := $(patsubst %,$(LOCAL_PATH)/%,$(LOCAL_SRC_FILES))
+out_dir := $(OUT_DOCS)/$(LOCAL_MODULE)
+full_target := $(OUT_DOCS)/$(LOCAL_MODULE)-timestamp
+
+ifeq ($(LOCAL_DROIDDOC_SOURCE_PATH),)
+LOCAL_DROIDDOC_SOURCE_PATH := $(LOCAL_PATH)
+endif
+
+ifeq ($(LOCAL_DROIDDOC_TEMPLATE_DIR),)
+LOCAL_DROIDDOC_TEMPLATE_DIR := $(SRC_DROIDDOC_DIR)/templates
+endif
+ifeq ($(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR),)
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR := $(SRC_DROIDDOC_DIR)/templates
+endif
+
+ifeq ($(LOCAL_DROIDDOC_ASSET_DIR),)
+LOCAL_DROIDDOC_ASSET_DIR := assets
+endif
+ifeq ($(LOCAL_DROIDDOC_CUSTOM_ASSET_DIR),)
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR := assets
+endif
+
+droiddoc_templates := \
+ $(shell find $(LOCAL_DROIDDOC_TEMPLATE_DIR) -type f) \
+ $(shell find $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR) -type f)
+
+droiddoc := \
+ $(HOST_JDK_TOOLS_JAR) \
+ $(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX) \
+ $(HOST_OUT_SHARED_LIBRARIES)/libclearsilver-jni$(HOST_JNILIB_SUFFIX)
+
+intermediates := $(call local-intermediates-dir)
+
+$(full_target): PRIVATE_CLASSPATH:=$(LOCAL_CLASSPATH)
+full_java_lib_deps :=
+
+ifndef LOCAL_IS_HOST_MODULE
+
+ifeq ($(LOCAL_JAVA_LIBRARIES),)
+LOCAL_JAVA_LIBRARIES := core ext framework
+endif
+full_java_libs := $(call java-lib-files,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
+full_java_lib_deps := $(call java-lib-deps,$(LOCAL_JAVA_LIBRARIES),$(LOCAL_IS_HOST_MODULE))
+
+# we're not going to generate docs from any of these classes, but we need them
+# to build properly.
+ifneq ($(strip $(LOCAL_STATIC_JAVA_LIBRARIES)),)
+full_java_libs += $(addprefix $(LOCAL_PATH)/,$(LOCAL_STATIC_JAVA_LIBRARIES)) $(LOCAL_CLASSPATH)
+full_java_lib_deps += $(addprefix $(LOCAL_PATH)/,$(LOCAL_STATIC_JAVA_LIBRARIES)) $(LOCAL_CLASSPATH)
+endif
+
+empty :=
+space := $(empty) $(empty)
+$(full_target): PRIVATE_CLASSPATH := $(subst $(space),:,$(full_java_libs))
+
+endif # !LOCAL_IS_HOST_MODULE
+
+$(full_target): PRIVATE_DOCLETPATH := $(HOST_OUT_JAVA_LIBRARIES)/clearsilver$(COMMON_JAVA_PACKAGE_SUFFIX):$(HOST_OUT_JAVA_LIBRARIES)/droiddoc$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(full_target): PRIVATE_JAVA_FILES := $(filter %.java,$(full_src_files))
+$(full_target): PRIVATE_JAVA_FILES += $(addprefix $($(my_prefix)OUT_COMMON_INTERMEDIATES)/, $(filter %.java,$(LOCAL_INTERMEDIATE_SOURCES)))
+$(full_target): PRIVATE_CURRENT_BUILD := -hdf page.build $(BUILD_ID)-$(BUILD_NUMBER)
+$(full_target): PRIVATE_CURRENT_TIME := -hdf page.now "$(shell date "+%d %b %Y %k:%M")"
+$(full_target): PRIVATE_OUT_DIR := $(out_dir)
+$(full_target): PRIVATE_DROIDDOC_OPTIONS := $(LOCAL_DROIDDOC_OPTIONS)
+$(full_target): PRIVATE_TEMPLATE_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)
+$(full_target): PRIVATE_CUSTOM_TEMPLATE_DIR := $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR)
+$(full_target): PRIVATE_IN_ASSET_DIR := $(LOCAL_DROIDDOC_TEMPLATE_DIR)/$(LOCAL_DROIDDOC_ASSET_DIR)
+$(full_target): PRIVATE_IN_CUSTOM_ASSET_DIR := $(LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR)/$(LOCAL_DROIDDOC_CUSTOM_ASSET_DIR)
+$(full_target): PRIVATE_OUT_ASSET_DIR := $(out_dir)/$(LOCAL_DROIDDOC_ASSET_DIR)
+$(full_target): PRIVATE_OUT_CUSTOM_ASSET_DIR := $(out_dir)/$(LOCAL_DROIDDOC_CUSTOM_ASSET_DIR)
+ifneq ($(strip $(LOCAL_DROIDDOC_HTML_DIR)),)
+$(full_target): PRIVATE_DROIDDOC_HTML_DIR := -htmldir $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR)
+else
+$(full_target): PRIVATE_DROIDDOC_HTML_DIR :=
+endif
+$(full_target): PRIVATE_LOCAL_PATH := $(LOCAL_PATH)
+$(full_target): PRIVATE_SOURCE_PATH := $(call normalize-path-list,$(LOCAL_DROIDDOC_SOURCE_PATH))
+$(full_target): PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates)/src
+$(full_target): PRIVATE_SRC_LIST_FILE := $(intermediates)/droiddoc-src-list
+
+ifneq ($(strip $(LOCAL_ADDITIONAL_JAVA_DIR)),)
+$(full_target): PRIVATE_ADDITIONAL_JAVA_DIR := $(LOCAL_ADDITIONAL_JAVA_DIR)
+endif
+
+html_dir_files := $(shell find $(LOCAL_PATH)/$(LOCAL_DROIDDOC_HTML_DIR) -type f)
+
+ifeq (a,b)
+$(full_target): PRIVATE_PROFILING_OPTIONS := \
+ -J-agentlib:jprofilerti=port=8849 -J-Xbootclasspath/a:/Applications/jprofiler5/bin/agent.jar
+endif
+
+$(full_target): $(full_src_files) $(droiddoc_templates) $(droiddoc) $(html_dir_files) $(full_java_lib_deps)
+ @echo Docs droiddoc: $(PRIVATE_OUT_DIR)
+ @mkdir -p $(dir $(full_target))
+ @mkdir -p $(dir $(PRIVATE_SRC_LIST_FILE))
+ $(call dump-words-to-file, $(PRIVATE_JAVA_FILES), $(PRIVATE_SRC_LIST_FILE))
+ $(hide) find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) -name '*.java' >> $(PRIVATE_SRC_LIST_FILE) 2> /dev/null || true
+ $(hide) if [ "$(PRIVATE_ADDITIONAL_JAVA_DIR)" != "" ] ; then ( find $(PRIVATE_ADDITIONAL_JAVA_DIR) -name '*.java' >> $(PRIVATE_SRC_LIST_FILE) 2> /dev/null || true ) fi
+ $(hide) ( \
+ \
+ LD_LIBRARY_PATH=$(HOST_OUT_SHARED_LIBRARIES) \
+ javadoc \
+ \@$(PRIVATE_SRC_LIST_FILE) \
+ -J-Xmx768m \
+ -J-Djava.library.path=$(HOST_OUT_SHARED_LIBRARIES) \
+ $(PRIVATE_PROFILING_OPTIONS) \
+ -quiet \
+ -doclet DroidDoc \
+ -docletpath $(PRIVATE_DOCLETPATH) \
+ -templatedir $(PRIVATE_CUSTOM_TEMPLATE_DIR) \
+ -templatedir $(PRIVATE_TEMPLATE_DIR) \
+ $(PRIVATE_DROIDDOC_HTML_DIR) \
+ $(addprefix -classpath ,$(PRIVATE_CLASSPATH)) \
+ -sourcepath $(PRIVATE_SOURCE_PATH)$(addprefix :,$(PRIVATE_CLASSPATH)) \
+ -d $(PRIVATE_OUT_DIR) \
+ $(PRIVATE_CURRENT_BUILD) $(PRIVATE_CURRENT_TIME) \
+ $(PRIVATE_DROIDDOC_OPTIONS) \
+ && rm -rf $(PRIVATE_OUT_ASSET_DIR) \
+ && rm -rf $(PRIVATE_OUT_CUSTOM_ASSET_DIR) \
+ && mkdir -p $(PRIVATE_OUT_ASSET_DIR) \
+ && mkdir -p $(PRIVATE_OUT_CUSTOM_ASSET_DIR) \
+ && cp -fr $(PRIVATE_IN_ASSET_DIR)/* $(PRIVATE_OUT_ASSET_DIR)/ \
+ && cp -fr $(PRIVATE_IN_CUSTOM_ASSET_DIR)/* $(PRIVATE_OUT_CUSTOM_ASSET_DIR)/ \
+ && touch -f $@ \
+ ) || (rm -rf $(PRIVATE_OUT_DIR) $(PRIVATE_SRC_LIST_FILE); exit 45)
+
+ALL_DOCS += $(full_target)
+
+.PHONY: $(LOCAL_MODULE)-docs
+$(LOCAL_MODULE)-docs : $(full_target)
+
+# Define a rule to create a zip of these docs.
+out_zip := $(OUT_DOCS)/$(LOCAL_MODULE)-docs.zip
+$(out_zip): PRIVATE_DOCS_DIR := $(out_dir)
+$(out_zip): $(full_target)
+ @echo Package docs: $@
+ @rm -f $@
+ @mkdir -p $(dir $@)
+ $(hide) ( F=$$(pwd)/$@ ; cd $(PRIVATE_DOCS_DIR) && zip -rq $$F * )
+
+$(call dist-for-goals,docs,$(out_zip))
diff --git a/core/dynamic_binary.mk b/core/dynamic_binary.mk
new file mode 100644
index 000000000..10027b828
--- /dev/null
+++ b/core/dynamic_binary.mk
@@ -0,0 +1,144 @@
+###########################################################
+## Standard rules for building any target-side binaries
+## with dynamic linkage (dynamic libraries or executables
+## that link with dynamic libraries)
+##
+## Files including this file must define a rule to build
+## the target $(linked_module).
+###########################################################
+
+# This constraint means that we can hard-code any $(TARGET_*) variables.
+ifdef LOCAL_IS_HOST_MODULE
+$(error This file should not be used to build host binaries. Included by (or near) $(lastword $(filter-out config/%,$(MAKEFILE_LIST))))
+endif
+
+LOCAL_UNSTRIPPED_PATH := $(strip $(LOCAL_UNSTRIPPED_PATH))
+ifeq ($(LOCAL_UNSTRIPPED_PATH),)
+ LOCAL_UNSTRIPPED_PATH := $(TARGET_OUT_$(LOCAL_MODULE_CLASS)_UNSTRIPPED)
+endif
+
+# The name of the target file, without any path prepended.
+LOCAL_BUILT_MODULE_STEM := $(LOCAL_MODULE)$(LOCAL_MODULE_SUFFIX)
+
+# base_rules.make defines $(intermediates), but we need its value
+# before we include base_rules. Make a guess, and verify that
+# it's correct once the real value is defined.
+guessed_intermediates := $(call local-intermediates-dir)
+
+# Define the target that is the unmodified output of the linker.
+# The basename of this target must be the same as the final output
+# binary name, because it's used to set the "soname" in the binary.
+# The includer of this file will define a rule to build this target.
+linked_module := $(guessed_intermediates)/LINKED/$(LOCAL_BUILT_MODULE_STEM)
+
+ALL_ORIGINAL_DYNAMIC_BINARIES += $(linked_module)
+
+# Because TARGET_SYMBOL_FILTER_FILE depends on ALL_ORIGINAL_DYNAMIC_BINARIES,
+# the linked_module rules won't necessarily inherit the PRIVATE_
+# variables from LOCAL_BUILT_MODULE. This tells binary.make to explicitly
+# define the PRIVATE_ variables for linked_module as well as for
+# LOCAL_BUILT_MODULE.
+LOCAL_INTERMEDIATE_TARGETS := $(linked_module)
+
+###################################
+include $(BUILD_SYSTEM)/binary.mk
+###################################
+
+# Make sure that our guess at the value of intermediates was correct.
+ifneq ($(intermediates),$(guessed_intermediates))
+$(error Internal error: guessed path '$(guessed_intermediates)' doesn't match '$(intermediates))
+endif
+
+###########################################################
+## Compress
+###########################################################
+compress_input := $(linked_module)
+
+ifeq ($(strip $(LOCAL_COMPRESS_MODULE_SYMBOLS)),)
+ LOCAL_COMPRESS_MODULE_SYMBOLS := $(strip $(TARGET_COMPRESS_MODULE_SYMBOLS))
+endif
+
+ifeq ($(LOCAL_COMPRESS_MODULE_SYMBOLS),true)
+$(error Symbol compression not yet supported.)
+compress_output := $(intermediates)/COMPRESSED-$(LOCAL_BUILT_MODULE_STEM)
+
+#TODO: write the real $(SOSLIM) rule.
+#TODO: define a rule to build TARGET_SYMBOL_FILTER_FILE, and
+# make it depend on ALL_ORIGINAL_DYNAMIC_BINARIES.
+$(compress_output): $(compress_input) $(TARGET_SYMBOL_FILTER_FILE) | $(ACP)
+ @echo "target Compress Symbols: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target)
+else
+# Skip this step.
+compress_output := $(compress_input)
+endif
+
+
+###########################################################
+## Pre-link
+###########################################################
+prelink_input := $(compress_output)
+# The output of the prelink step is the binary we want to use
+# for symbolic debugging; the prelink step may move sections
+# around, so we have to use this version.
+prelink_output := $(LOCAL_UNSTRIPPED_PATH)/$(LOCAL_MODULE_SUBDIR)$(LOCAL_BUILT_MODULE_STEM)
+
+ifeq ($(LOCAL_PRELINK_MODULE),true)
+$(prelink_output): $(prelink_input) $(TARGET_PRELINKER_MAP) $(APRIORI)
+ $(transform-to-prelinked)
+else
+# Don't prelink the binary, just copy it. We can't skip this step
+# because people always expect a copy of the binary to appear
+# in the UNSTRIPPED directory.
+#
+# If the binary we're copying is acp or a prerequisite,
+# use cp(1) instead.
+ifneq ($(LOCAL_ACP_UNAVAILABLE),true)
+$(prelink_output): $(prelink_input) | $(ACP)
+ @echo "target Non-prelinked: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target)
+else
+$(prelink_output): $(prelink_input)
+ @echo "target Non-prelinked: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target-with-cp)
+endif
+endif
+
+
+###########################################################
+## Strip
+###########################################################
+strip_input := $(prelink_output)
+strip_output := $(LOCAL_BUILT_MODULE)
+
+ifeq ($(strip $(LOCAL_STRIP_MODULE)),)
+ LOCAL_STRIP_MODULE := $(strip $(TARGET_STRIP_MODULE))
+endif
+
+ifeq ($(LOCAL_STRIP_MODULE),true)
+# Strip the binary
+$(strip_output): $(strip_input) | $(SOSLIM)
+ $(transform-to-stripped)
+else
+# Don't strip the binary, just copy it. We can't skip this step
+# because a copy of the binary must appear at LOCAL_BUILT_MODULE.
+#
+# If the binary we're copying is acp or a prerequisite,
+# use cp(1) instead.
+ifneq ($(LOCAL_ACP_UNAVAILABLE),true)
+$(strip_output): $(strip_input) | $(ACP)
+ @echo "target Unstripped: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target)
+else
+$(strip_output): $(strip_input)
+ @echo "target Unstripped: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target-with-cp)
+endif
+endif # LOCAL_STRIP_MODULE
+
+
+$(cleantarget): PRIVATE_CLEAN_FILES := \
+ $(PRIVATE_CLEAN_FILES) \
+ $(linked_module) \
+ $(compress_output) \
+ $(prelink_output)
diff --git a/core/envsetup.mk b/core/envsetup.mk
new file mode 100644
index 000000000..0c24ea9ca
--- /dev/null
+++ b/core/envsetup.mk
@@ -0,0 +1,333 @@
+# Variables we check:
+# HOST_BUILD_TYPE = { release debug }
+# TARGET_SIMULATOR = { true <null> }
+# TARGET_BUILD_TYPE = { release debug }
+# and we output a bunch of variables, see the case statement at
+# the bottom for the full list
+# OUT_DIR is also set to "out" if it's not already set.
+# this allows you to set it to somewhere else if you like
+
+# ---------------------------------------------------------------
+# If you update the build system such that the environment setup
+# or buildspec.mk need to be updated, increment this number, and
+# people who haven't re-run those will have to do so before they
+# can build. Make sure to also update the corresponding value in
+# buildspec.mk.default and envsetup.sh.
+CORRECT_BUILD_ENV_SEQUENCE_NUMBER := 9
+
+# ---------------------------------------------------------------
+# The product defaults to generic on hardware and sim on sim
+# NOTE: This will be overridden in product_config.mk if make
+# was invoked with a PRODUCT-xxx-yyy goal.
+ifeq ($(TARGET_PRODUCT),)
+ifeq ($(TARGET_SIMULATOR),true)
+TARGET_PRODUCT := sim
+else
+TARGET_PRODUCT := generic
+endif
+endif
+
+
+# the variant -- the set of files that are included for a build
+ifeq ($(strip $(TARGET_BUILD_VARIANT)),)
+TARGET_BUILD_VARIANT := eng
+endif
+
+# Read the product specs so we an get TARGET_DEVICE and other
+# variables that we need in order to locate the output files.
+include $(BUILD_SYSTEM)/product_config.mk
+
+build_variant := $(filter-out eng user userdebug tests,$(TARGET_BUILD_VARIANT))
+ifneq ($(build_variant)-$(words $(TARGET_BUILD_VARIANT)),-1)
+$(warning bad TARGET_BUILD_VARIANT: $(TARGET_BUILD_VARIANT))
+$(error must be empty or one of: eng user userdebug tests)
+endif
+
+
+
+# ---------------------------------------------------------------
+# Set up configuration for host machine. We don't do cross-
+# compiles except for arm, so the HOST is whatever we are
+# running on
+
+UNAME := $(shell uname -sm)
+
+# HOST_OS
+ifneq (,$(findstring Linux,$(UNAME)))
+ HOST_OS := linux
+endif
+ifneq (,$(findstring Darwin,$(UNAME)))
+ HOST_OS := darwin
+endif
+ifneq (,$(findstring Macintosh,$(UNAME)))
+ HOST_OS := darwin
+endif
+ifneq (,$(findstring CYGWIN,$(UNAME)))
+ HOST_OS := windows
+endif
+ifneq ($(USE_MINGW),)
+ HOST_OS := windows
+endif
+
+ifeq ($(HOST_OS),)
+$(error Unable to determine HOST_OS from uname -sm: $(UNAME)!)
+endif
+
+
+# HOST_ARCH
+ifneq (,$(findstring 86,$(UNAME)))
+ HOST_ARCH := x86
+endif
+
+ifneq (,$(findstring Power,$(UNAME)))
+ HOST_ARCH := ppc
+endif
+
+ifeq ($(HOST_ARCH),)
+$(error Unable to determine HOST_ARCH from uname -sm: $(UNAME)!)
+endif
+
+# the host build defaults to release, and it must be release or debug
+ifeq ($(HOST_BUILD_TYPE),)
+HOST_BUILD_TYPE := release
+endif
+
+ifneq ($(HOST_BUILD_TYPE),release)
+ifneq ($(HOST_BUILD_TYPE),debug)
+$(error HOST_BUILD_TYPE must be either release or debug, not '$(HOST_BUILD_TYPE)')
+endif
+endif
+
+# This is the standard way to name a directory containing prebuilt host
+# objects. E.g., prebuilt/$(HOST_PREBUILT_TAG)/cc
+ifeq ($(HOST_OS),windows)
+ HOST_PREBUILT_TAG := windows
+else
+ HOST_PREBUILT_TAG := $(HOST_OS)-$(HOST_ARCH)
+endif
+
+
+# ---------------------------------------------------------------
+# Set up configuration for target machine.
+# The following must be set:
+# TARGET_OS = { linux }
+# TARGET_ARCH = { arm | x86 }
+
+
+# if we're build the simulator, HOST_* is TARGET_* (except for BUILD_TYPE)
+# otherwise it's <arch>-linux
+ifeq ($(TARGET_SIMULATOR),true)
+ifneq ($(HOST_OS),linux)
+$(error TARGET_SIMULATOR=true is only supported under Linux)
+endif
+TARGET_ARCH := $(HOST_ARCH)
+TARGET_OS := $(HOST_OS)
+else
+ifeq ($(TARGET_ARCH),)
+TARGET_ARCH := arm
+endif
+TARGET_OS := linux
+endif
+
+# the target build type defaults to release
+ifneq ($(TARGET_BUILD_TYPE),debug)
+TARGET_BUILD_TYPE := release
+endif
+
+# This is the standard way to name a directory containing prebuilt target
+# objects. E.g., prebuilt/$(TARGET_PREBUILT_TAG)/libc.so
+ifeq ($(TARGET_SIMULATOR),true)
+ TARGET_PREBUILT_TAG := $(TARGET_OS)-$(TARGET_ARCH)
+else
+ TARGET_PREBUILT_TAG := android-$(TARGET_ARCH)
+endif
+
+# ---------------------------------------------------------------
+# figure out the output directories
+
+ifeq (,$(strip $(OUT_DIR)))
+OUT_DIR := $(TOPDIR)out
+endif
+
+DEBUG_OUT_DIR := $(OUT_DIR)/debug
+
+# Move the host or target under the debug/ directory
+# if necessary.
+TARGET_OUT_ROOT_release := $(OUT_DIR)/target
+TARGET_OUT_ROOT_debug := $(DEBUG_OUT_DIR)/target
+TARGET_OUT_ROOT := $(TARGET_OUT_ROOT_$(TARGET_BUILD_TYPE))
+
+HOST_OUT_ROOT_release := $(OUT_DIR)/host
+HOST_OUT_ROOT_debug := $(DEBUG_OUT_DIR)/host
+HOST_OUT_ROOT := $(HOST_OUT_ROOT_$(HOST_BUILD_TYPE))
+
+HOST_OUT_release := $(HOST_OUT_ROOT_release)/$(HOST_OS)-$(HOST_ARCH)
+HOST_OUT_debug := $(HOST_OUT_ROOT_debug)/$(HOST_OS)-$(HOST_ARCH)
+HOST_OUT := $(HOST_OUT_$(HOST_BUILD_TYPE))
+
+ifeq ($(TARGET_SIMULATOR),true)
+ # Any arch- or os-specific parts of the simulator (everything
+ # under product/) are actually host-dependent.
+ # But, the debug type is controlled by TARGET_BUILD_TYPE and not
+ # HOST_BUILD_TYPE.
+ TARGET_PRODUCT_OUT_ROOT := $(HOST_OUT_$(TARGET_BUILD_TYPE))/product
+else
+ TARGET_PRODUCT_OUT_ROOT := $(TARGET_OUT_ROOT)/product
+endif
+
+TARGET_COMMON_OUT_ROOT := $(TARGET_OUT_ROOT)/common
+HOST_COMMON_OUT_ROOT := $(HOST_OUT_ROOT)/common
+
+PRODUCT_OUT := $(TARGET_PRODUCT_OUT_ROOT)/$(TARGET_DEVICE)
+
+OUT_DOCS := $(TARGET_COMMON_OUT_ROOT)/docs
+
+HOST_OUT_EXECUTABLES:= $(HOST_OUT)/bin
+HOST_OUT_SHARED_LIBRARIES:= $(HOST_OUT)/lib
+HOST_OUT_JAVA_LIBRARIES:= $(HOST_OUT)/framework
+
+HOST_OUT_INTERMEDIATES := $(HOST_OUT)/obj
+HOST_OUT_HEADERS:= $(HOST_OUT_INTERMEDIATES)/include
+HOST_OUT_INTERMEDIATE_LIBRARIES := $(HOST_OUT_INTERMEDIATES)/lib
+HOST_OUT_STATIC_LIBRARIES := $(HOST_OUT_INTERMEDIATE_LIBRARIES)
+HOST_OUT_NOTICE_FILES:=$(HOST_OUT_INTERMEDIATES)/NOTICE_FILES
+HOST_OUT_COMMON_INTERMEDIATES := $(HOST_COMMON_OUT_ROOT)/obj
+
+TARGET_OUT_INTERMEDIATES := $(PRODUCT_OUT)/obj
+TARGET_OUT_HEADERS:= $(TARGET_OUT_INTERMEDIATES)/include
+TARGET_OUT_INTERMEDIATE_LIBRARIES := $(TARGET_OUT_INTERMEDIATES)/lib
+TARGET_OUT_COMMON_INTERMEDIATES := $(TARGET_COMMON_OUT_ROOT)/obj
+
+TARGET_OUT := $(PRODUCT_OUT)/system
+TARGET_OUT_EXECUTABLES:= $(TARGET_OUT)/bin
+TARGET_OUT_OPTIONAL_EXECUTABLES:= $(TARGET_OUT)/xbin
+TARGET_OUT_SHARED_LIBRARIES:= $(TARGET_OUT)/lib
+TARGET_OUT_JAVA_LIBRARIES:= $(TARGET_OUT)/framework
+TARGET_OUT_APPS:= $(TARGET_OUT)/app
+TARGET_OUT_KEYLAYOUT := $(TARGET_OUT)/usr/keylayout
+TARGET_OUT_KEYCHARS := $(TARGET_OUT)/usr/keychars
+TARGET_OUT_ETC := $(TARGET_OUT)/etc
+TARGET_OUT_STATIC_LIBRARIES:= $(TARGET_OUT_INTERMEDIATES)/lib
+TARGET_OUT_NOTICE_FILES:=$(TARGET_OUT_INTERMEDIATES)/NOTICE_FILES
+
+TARGET_OUT_DATA := $(PRODUCT_OUT)/data
+TARGET_OUT_DATA_EXECUTABLES:= $(TARGET_OUT_EXECUTABLES)
+TARGET_OUT_DATA_SHARED_LIBRARIES:= $(TARGET_OUT_SHARED_LIBRARIES)
+TARGET_OUT_DATA_JAVA_LIBRARIES:= $(TARGET_OUT_JAVA_LIBRARIES)
+TARGET_OUT_DATA_APPS:= $(TARGET_OUT_DATA)/app
+TARGET_OUT_DATA_KEYLAYOUT := $(TARGET_OUT_KEYLAYOUT)
+TARGET_OUT_DATA_KEYCHARS := $(TARGET_OUT_KEYCHARS)
+TARGET_OUT_DATA_ETC := $(TARGET_OUT_ETC)
+TARGET_OUT_DATA_STATIC_LIBRARIES:= $(TARGET_OUT_STATIC_LIBRARIES)
+
+TARGET_OUT_UNSTRIPPED := $(PRODUCT_OUT)/symbols
+TARGET_OUT_EXECUTABLES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/bin
+TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/system/lib
+TARGET_ROOT_OUT_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)
+TARGET_ROOT_OUT_SBIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/sbin
+TARGET_ROOT_OUT_BIN_UNSTRIPPED := $(TARGET_OUT_UNSTRIPPED)/bin
+
+TARGET_ROOT_OUT := $(PRODUCT_OUT)/root
+TARGET_ROOT_OUT_BIN := $(TARGET_ROOT_OUT)/bin
+TARGET_ROOT_OUT_SBIN := $(TARGET_ROOT_OUT)/sbin
+TARGET_ROOT_OUT_ETC := $(TARGET_ROOT_OUT)/etc
+TARGET_ROOT_OUT_USR := $(TARGET_ROOT_OUT)/usr
+
+TARGET_RECOVERY_OUT := $(PRODUCT_OUT)/recovery
+TARGET_RECOVERY_ROOT_OUT := $(TARGET_RECOVERY_OUT)/root
+
+TARGET_SYSLOADER_OUT := $(PRODUCT_OUT)/sysloader
+TARGET_SYSLOADER_ROOT_OUT := $(TARGET_SYSLOADER_OUT)/root
+TARGET_SYSLOADER_SYSTEM_OUT := $(TARGET_SYSLOADER_OUT)/root/system
+
+TARGET_INSTALLER_OUT := $(PRODUCT_OUT)/installer
+TARGET_INSTALLER_DATA_OUT := $(TARGET_INSTALLER_OUT)/data
+TARGET_INSTALLER_ROOT_OUT := $(TARGET_INSTALLER_OUT)/root
+TARGET_INSTALLER_SYSTEM_OUT := $(TARGET_INSTALLER_OUT)/root/system
+
+COMMON_MODULE_CLASSES := JAVA_LIBRARIES NOTICE_FILES
+
+ifeq (,$(strip $(DIST_DIR)))
+ DIST_DIR := $(OUT_DIR)/dist
+endif
+
+ifeq ($(PRINT_BUILD_CONFIG),)
+PRINT_BUILD_CONFIG := true
+endif
+
+# ---------------------------------------------------------------
+# the setpath shell function in envsetup.sh uses this to figure out
+# what to add to the path given the config we have chosen.
+ifeq ($(CALLED_FROM_SETUP),true)
+
+ABP:=$(PWD)/$(HOST_OUT_EXECUTABLES)
+
+ifeq ($(TARGET_SIMULATOR),true)
+ ABP:=$(ABP):$(TARGET_OUT_EXECUTABLES)
+else
+ # this should be copied to HOST_OUT_EXECUTABLES instead
+ ABP:=$(ABP):$(PWD)/prebuilt/$(HOST_PREBUILT_TAG)/toolchain/arm-eabi-4.2.1/bin
+endif
+ANDROID_BUILD_PATHS := $(ABP)
+ANDROID_PREBUILTS := prebuilt/$(HOST_PREBUILT_TAG)
+
+# The "dumpvar" stuff lets you say something like
+#
+# CALLED_FROM_SETUP=true \
+# make -f config/envsetup.make dumpvar-TARGET_OUT
+# or
+# CALLED_FROM_SETUP=true \
+# make -f config/envsetup.make dumpvar-abs-HOST_OUT_EXECUTABLES
+#
+# The plain (non-abs) version just dumps the value of the named variable.
+# The "abs" version will treat the variable as a path, and dumps an
+# absolute path to it.
+#
+dumpvar_goals := \
+ $(strip $(patsubst dumpvar-%,%,$(filter dumpvar-%,$(MAKECMDGOALS))))
+ifdef dumpvar_goals
+
+ ifneq ($(words $(dumpvar_goals)),1)
+ $(error Only one "dumpvar-" goal allowed. Saw "$(MAKECMDGOALS)")
+ endif
+
+ # If the goal is of the form "dumpvar-abs-VARNAME", then
+ # treat VARNAME as a path and return the absolute path to it.
+ absolute_dumpvar := $(strip $(filter abs-%,$(dumpvar_goals)))
+ ifdef absolute_dumpvar
+ dumpvar_goals := $(patsubst abs-%,%,$(dumpvar_goals))
+ DUMPVAR_VALUE := $(PWD)/$($(dumpvar_goals))
+ dumpvar_target := dumpvar-abs-$(dumpvar_goals)
+ else
+ DUMPVAR_VALUE := $($(dumpvar_goals))
+ dumpvar_target := dumpvar-$(dumpvar_goals)
+ endif
+
+.PHONY: $(dumpvar_target)
+$(dumpvar_target):
+ @echo $(DUMPVAR_VALUE)
+
+endif # dumpvar_goals
+
+ifneq ($(dumpvar_goals),report_config)
+PRINT_BUILD_CONFIG:=
+endif
+
+endif # CALLED_FROM_SETUP
+
+
+ifneq ($(PRINT_BUILD_CONFIG),)
+$(info ============================================)
+$(info TARGET_PRODUCT=$(TARGET_PRODUCT))
+$(info TARGET_BUILD_VARIANT=$(TARGET_BUILD_VARIANT))
+$(info TARGET_SIMULATOR=$(TARGET_SIMULATOR))
+$(info TARGET_BUILD_TYPE=$(TARGET_BUILD_TYPE))
+$(info TARGET_ARCH=$(TARGET_ARCH))
+$(info HOST_ARCH=$(HOST_ARCH))
+$(info HOST_OS=$(HOST_OS))
+$(info HOST_BUILD_TYPE=$(HOST_BUILD_TYPE))
+$(info BUILD_ID=$(BUILD_ID))
+$(info ============================================)
+endif
+
+
diff --git a/core/executable.mk b/core/executable.mk
new file mode 100644
index 000000000..623c76637
--- /dev/null
+++ b/core/executable.mk
@@ -0,0 +1,28 @@
+###########################################################
+## Standard rules for building an executable file.
+##
+## Additional inputs from base_rules.make:
+## None.
+###########################################################
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := EXECUTABLES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := $(TARGET_EXECUTABLE_SUFFIX)
+endif
+
+# Executables are not prelinked. If we decide to start prelinking
+# them, the LOCAL_PRELINK_MODULE definitions should be moved from
+# here and shared_library.make and consolidated in dynamic_binary.make.
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SYSTEM)/dynamic_binary.mk
+
+ifeq ($(LOCAL_FORCE_STATIC_EXECUTABLE),true)
+$(linked_module): $(TARGET_CRTBEGIN_STATIC_O) $(all_objects) $(all_libraries) $(TARGET_CRTEND_O)
+ $(transform-o-to-static-executable)
+else
+$(linked_module): $(TARGET_CRTBEGIN_DYNAMIC_O) $(all_objects) $(all_libraries) $(TARGET_CRTEND_O)
+ $(transform-o-to-executable)
+endif
diff --git a/core/filter_symbols.sh b/core/filter_symbols.sh
new file mode 100644
index 000000000..ba5057afd
--- /dev/null
+++ b/core/filter_symbols.sh
@@ -0,0 +1,25 @@
+NM=$1
+
+shift
+
+PREFIX=$1
+
+shift
+
+SUFFIX=$1
+
+shift
+
+while test "$1" != ""
+do
+ $NM -g -fp $1 | while read -a line
+ do
+ type=${line[1]}
+ # if [[ "$type" != "V" && "$type" != "U" ]]; then
+ #if [[ "$type" != "W" && "$type" != "V" && "$type" != "U" ]]; then
+ echo "$PREFIX${line[0]}$SUFFIX # ${line[1]}"
+ #fi
+ done
+
+ shift
+done
diff --git a/core/find-jdk-tools-jar.sh b/core/find-jdk-tools-jar.sh
new file mode 100755
index 000000000..091eae467
--- /dev/null
+++ b/core/find-jdk-tools-jar.sh
@@ -0,0 +1,10 @@
+if [[ "x$ANDROID_JAVA_HOME" != x && -e $ANDROID_JAVA_HOME/lib/tools.jar ]] ; then
+ echo $ANDROID_JAVA_HOME/lib/tools.jar
+else
+ JAVAC=$(which javac)
+ while [ -L $JAVAC ] ; do
+ LSLINE=$(ls -l $JAVAC)
+ JAVAC=$(echo -n $LSLINE | sed -e "s/.* -> //")
+ done
+ echo $JAVAC | sed -e "s:\(.*\)/bin/javac.*:\\1/lib/tools.jar:"
+fi
diff --git a/core/host_executable.mk b/core/host_executable.mk
new file mode 100644
index 000000000..4d90e6db5
--- /dev/null
+++ b/core/host_executable.mk
@@ -0,0 +1,20 @@
+###########################################################
+## Standard rules for building an executable file.
+##
+## Additional inputs from base_rules.make:
+## None.
+###########################################################
+
+LOCAL_IS_HOST_MODULE := true
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := EXECUTABLES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := $(HOST_EXECUTABLE_SUFFIX)
+endif
+
+include $(BUILD_SYSTEM)/binary.mk
+
+$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries)
+ $(transform-host-o-to-executable)
+ $(PRIVATE_POST_PROCESS_COMMAND)
diff --git a/core/host_java_library.mk b/core/host_java_library.mk
new file mode 100644
index 000000000..92b5ff661
--- /dev/null
+++ b/core/host_java_library.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Standard rules for building a host java library.
+#
+
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_BUILT_MODULE_STEM := javalib.jar
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(java_sources) $(java_resource_sources) $(full_java_lib_deps)
+ $(transform-host-java-to-package)
diff --git a/core/host_prebuilt.mk b/core/host_prebuilt.mk
new file mode 100644
index 000000000..7baab6995
--- /dev/null
+++ b/core/host_prebuilt.mk
@@ -0,0 +1,18 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_IS_HOST_MODULE := true
+include $(BUILD_MULTI_PREBUILT)
diff --git a/core/host_shared_library.mk b/core/host_shared_library.mk
new file mode 100644
index 000000000..f78b17b8a
--- /dev/null
+++ b/core/host_shared_library.mk
@@ -0,0 +1,29 @@
+###########################################################
+## Standard rules for building a normal shared library.
+##
+## Additional inputs from base_rules.make:
+## None.
+##
+## LOCAL_MODULE_SUFFIX will be set for you.
+###########################################################
+
+LOCAL_IS_HOST_MODULE := true
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := $(HOST_SHLIB_SUFFIX)
+endif
+ifneq ($(strip $(OVERRIDE_BUILT_MODULE_PATH)),)
+$(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
+endif
+
+# Put the built modules of all shared libraries in a common directory
+# to simplify the link line.
+OVERRIDE_BUILT_MODULE_PATH := $(HOST_OUT_INTERMEDIATE_LIBRARIES)
+
+include $(BUILD_SYSTEM)/binary.mk
+
+$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries) $(LOCAL_ADDITIONAL_DEPENDENCIES)
+ $(transform-host-o-to-shared-lib)
diff --git a/core/host_static_library.mk b/core/host_static_library.mk
new file mode 100644
index 000000000..237981f63
--- /dev/null
+++ b/core/host_static_library.mk
@@ -0,0 +1,23 @@
+###########################################################
+## Standard rules for building a static library.
+##
+## Additional inputs from base_rules.make:
+## None.
+##
+## LOCAL_MODULE_SUFFIX will be set for you.
+###########################################################
+
+LOCAL_IS_HOST_MODULE := true
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := .a
+endif
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/binary.mk
+
+$(LOCAL_BUILT_MODULE): $(all_objects)
+ $(transform-host-o-to-static-lib)
diff --git a/core/java.mk b/core/java.mk
new file mode 100644
index 000000000..65c525d21
--- /dev/null
+++ b/core/java.mk
@@ -0,0 +1,178 @@
+# Requires:
+# LOCAL_MODULE_SUFFIX
+# LOCAL_MODULE_CLASS
+# all_res_assets
+
+# Make sure there's something to build.
+# It's possible to build a package that doesn't contain any classes.
+ifeq (,$(strip $(LOCAL_SRC_FILES)$(all_res_assets)))
+$(error $(LOCAL_PATH): Target java module does not define any source or resource files)
+endif
+
+LOCAL_NO_STANDARD_LIBRARIES:=$(strip $(LOCAL_NO_STANDARD_LIBRARIES))
+LOCAL_SDK_VERSION:=$(strip $(LOCAL_SDK_VERSION))
+
+ifneq ($(LOCAL_SDK_VERSION),)
+ ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)
+ $(error $(LOCAL_PATH): Must not define both LOCAL_NO_STANDARD_LIBRARIES and LOCAL_SDK_VERSION)
+ else
+ ifeq ($(strip $(filter $(LOCAL_SDK_VERSION),$(TARGET_AVAILABLE_SDK_VERSIONS))),)
+ $(error $(LOCAL_PATH): Invalid LOCAL_SDK_VERSION '$(LOCAL_SDK_VERSION)' \
+ Choices are: $(TARGET_AVAILABLE_SDK_VERSIONS))
+ else
+ LOCAL_JAVA_LIBRARIES := android_stubs_$(LOCAL_SDK_VERSION) $(LOCAL_JAVA_LIBRARIES)
+ endif
+ endif
+else
+ ifneq ($(LOCAL_NO_STANDARD_LIBRARIES),true)
+ LOCAL_JAVA_LIBRARIES := core ext framework $(LOCAL_JAVA_LIBRARIES)
+ endif
+endif
+
+LOCAL_BUILT_MODULE_STEM := $(strip $(LOCAL_BUILT_MODULE_STEM))
+ifeq ($(LOCAL_BUILT_MODULE_STEM),)
+$(error $(LOCAL_PATH): Target java template must define LOCAL_BUILT_MODULE_STEM)
+endif
+ifneq ($(filter classes-compiled.jar classes.jar,$(LOCAL_BUILT_MODULE_STEM)),)
+$(error LOCAL_BUILT_MODULE_STEM may not be "$(LOCAL_BUILT_MODULE_STEM)")
+endif
+
+#######################################
+include $(BUILD_SYSTEM)/base_rules.mk
+#######################################
+
+# We use intermediates.COMMON because the classes.jar/.dex files will be
+# common even if LOCAL_BUILT_MODULE isn't.
+#
+# Override some target variables that base_rules set up for us.
+$(LOCAL_INTERMEDIATE_TARGETS): \
+ PRIVATE_CLASS_INTERMEDIATES_DIR := $(intermediates.COMMON)/classes
+$(LOCAL_INTERMEDIATE_TARGETS): \
+ PRIVATE_SOURCE_INTERMEDIATES_DIR := $(intermediates.COMMON)/src
+
+# Since we're using intermediates.COMMON, make sure that it gets cleaned
+# properly.
+$(cleantarget): PRIVATE_CLEAN_FILES += $(intermediates.COMMON)
+
+# If the module includes java code (i.e., it's not framework-res), compile it.
+full_classes_jar :=
+built_dex :=
+ifneq (,$(strip $(all_java_sources)))
+
+# If LOCAL_BUILT_MODULE_STEM wasn't overridden by our caller,
+# full_classes_jar will be the same module as LOCAL_BUILT_MODULE.
+# Otherwise, the caller will define it as a prerequisite of
+# LOCAL_BUILT_MODULE, so it will inherit the necessary PRIVATE_*
+# variable definitions.
+full_classes_jar := $(intermediates.COMMON)/classes.jar
+
+# Emma source code coverage
+ifneq ($(EMMA_INSTRUMENT),true)
+LOCAL_NO_EMMA_INSTRUMENT := true
+LOCAL_NO_EMMA_COMPILE := true
+endif
+
+ifneq ($(LOCAL_NO_EMMA_COMPILE),true)
+# If you instrument class files that have local variable debug information in
+# them emma does not correctly maintain the local variable table.
+# This will cause an error when you try to convert the class files for Android.
+# The workaround for this to compile the java classes with only
+# line and source debug information, not local information.
+full_classes_compiled_name_jar := classes-no-debug-var.jar
+$(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g:{lines,source}
+else
+# when emma is off, compile with the default flags, which contain full debug
+# info
+full_classes_compiled_name_jar := classes-full-debug.jar
+$(full_classes_compiled_jar): PRIVATE_JAVAC_DEBUG_FLAGS := -g
+endif
+
+# Compile the java files to a .jar file.
+# This intentionally depends on java_sources, not all_java_sources.
+# Deps for generated source files must be handled separately,
+# via deps on the target that generates the sources.
+full_classes_compiled_jar := $(intermediates.COMMON)/$(full_classes_compiled_name_jar)
+$(full_classes_compiled_jar): $(java_sources) $(full_java_lib_deps)
+ $(transform-java-to-classes.jar)
+
+emma_intermediates_dir := $(intermediates.COMMON)/emma_out
+# the 'lib/$(full_classes_compiled_name_jar)' portion of this path is fixed in
+# the emma tool
+full_classes_emma_jar := $(emma_intermediates_dir)/lib/$(full_classes_compiled_name_jar)
+
+ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)
+# Skip adding emma instrumentation to class files if this is a static library,
+# since it will be instrumented by the package that includes it
+LOCAL_NO_EMMA_INSTRUMENT:= true
+endif
+
+ifneq ($(LOCAL_NO_EMMA_INSTRUMENT),true)
+$(full_classes_emma_jar): PRIVATE_EMMA_COVERAGE_FILE := $(intermediates.COMMON)/coverage.em
+$(full_classes_emma_jar): PRIVATE_EMMA_INTERMEDIATES_DIR := $(emma_intermediates_dir)
+# this rule will generate both $(PRIVATE_EMMA_COVERAGE_FILE) and
+# $(full_classes_emma_jar)
+$(full_classes_emma_jar): $(full_classes_compiled_jar)
+ $(transform-classes.jar-to-emma)
+$(PRIVATE_EMMA_COVERAGE_FILE): $(full_classes_emma_jar)
+else
+$(full_classes_emma_jar): $(full_classes_compiled_jar) | $(ACP)
+ @echo Copying $<
+ $(copy-file-to-target)
+endif
+
+# Run jarjar if necessary, otherwise just copy the file. This is the last
+# part of this step, so the output of this command is full_classes_jar.
+full_classes_jarjar_jar := $(full_classes_jar)
+ifneq ($(strip $(LOCAL_JARJAR_RULES)),)
+$(full_classes_jarjar_jar): PRIVATE_JARJAR_RULES := $(LOCAL_JARJAR_RULES)
+$(full_classes_jarjar_jar): $(full_classes_emma_jar) | jarjar
+ @echo JarJar: $@
+ $(hide) $(JARJAR) process $(PRIVATE_JARJAR_RULES) $< $@
+else
+$(full_classes_jarjar_jar): $(full_classes_emma_jar) | $(ACP)
+ @echo Copying: $@
+ $(hide) $(ACP) $< $@
+endif
+
+
+built_dex := $(intermediates.COMMON)/classes.dex
+
+# Override PRIVATE_INTERMEDIATES_DIR so that install-dex-debug
+# will work even when intermediates != intermediates.COMMON.
+$(built_dex): PRIVATE_INTERMEDIATES_DIR := $(intermediates.COMMON)
+$(built_dex): PRIVATE_DX_FLAGS := $(LOCAL_DX_FLAGS)
+$(built_dex): $(full_classes_jar) $(DX)
+ $(transform-classes.jar-to-dex)
+ifneq ($(GENERATE_DEX_DEBUG),)
+ $(install-dex-debug)
+endif
+
+findbugs_xml := $(intermediates.COMMON)/findbugs.xml
+$(findbugs_xml) : PRIVATE_JAR_FILE := $(full_classes_jar)
+$(findbugs_xml) : PRIVATE_AUXCLASSPATH := $(addprefix -auxclasspath ,$(strip \
+ $(call normalize-path-list,$(filter %.jar,\
+ $(full_java_libs)))))
+# We can't depend directly on full_classes_jar because the PRIVATE_
+# vars won't be set up correctly.
+$(findbugs_xml) : $(LOCAL_BUILT_MODULE)
+ @echo Findbugs: $@
+ $(hide) $(FINDBUGS) -textui -effort:min -xml:withMessages \
+ $(PRIVATE_AUXCLASSPATH) \
+ $(PRIVATE_JAR_FILE) \
+ > $@
+
+ALL_FINDBUGS_FILES += $(findbugs_xml)
+
+findbugs_html := $(PRODUCT_OUT)/findbugs/$(LOCAL_MODULE).html
+$(findbugs_html) : PRIVATE_XML_FILE := $(findbugs_xml)
+$(LOCAL_MODULE)-findbugs : $(findbugs_html)
+$(findbugs_html) : $(findbugs_xml)
+ @mkdir -p $(dir $@)
+ @echo UnionBugs: $@
+ $(hide) prebuilt/common/findbugs/bin/unionBugs $(PRIVATE_XML_FILE) \
+ | prebuilt/common/findbugs/bin/convertXmlToText -html:fancy.xsl \
+ > $@
+
+$(LOCAL_MODULE)-findbugs : $(findbugs_html)
+
+endif
diff --git a/core/java_library.mk b/core/java_library.mk
new file mode 100644
index 000000000..a33bf2eca
--- /dev/null
+++ b/core/java_library.mk
@@ -0,0 +1,47 @@
+###########################################################
+## Standard rules for building a java library.
+##
+###########################################################
+
+ifdef LOCAL_IS_HOST_MODULE
+$(error $(LOCAL_PATH): Host java libraries must use BUILD_HOST_JAVA_LIBRARY)
+endif
+
+LOCAL_MODULE_SUFFIX := $(COMMON_JAVA_PACKAGE_SUFFIX)
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+ifneq (,$(LOCAL_ASSET_DIR))
+$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_ASSET_DIR)
+endif
+
+ifneq (,$(LOCAL_RESOURCE_DIR))
+$(error $(LOCAL_PATH): Target java libraries may not set LOCAL_RESOURCE_DIR)
+endif
+
+#xxx base_rules.mk looks at this
+all_res_assets :=
+
+LOCAL_BUILT_MODULE_STEM := javalib.jar
+
+#################################
+include $(BUILD_SYSTEM)/java.mk
+#################################
+
+ifeq ($(LOCAL_IS_STATIC_JAVA_LIBRARY),true)
+# No dex or resources; all we want are the .class files.
+$(LOCAL_BUILT_MODULE): $(full_classes_jar)
+ @echo "target Static Jar: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-target)
+
+else # !LOCAL_IS_STATIC_JAVA_LIBRARY
+
+$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex)
+$(LOCAL_BUILT_MODULE): $(built_dex) $(java_resource_sources) | $(AAPT)
+ @echo "target Jar: $(PRIVATE_MODULE) ($@)"
+ $(create-empty-package)
+ $(add-dex-to-package)
+ifneq ($(extra_jar_args),)
+ $(add-java-resources-to-package)
+endif
+
+endif # !LOCAL_IS_STATIC_JAVA_LIBRARY
diff --git a/core/key_char_map.mk b/core/key_char_map.mk
new file mode 100644
index 000000000..0f112f251
--- /dev/null
+++ b/core/key_char_map.mk
@@ -0,0 +1,27 @@
+###########################################################
+## Standard rules for building an executable file.
+##
+## Additional inputs from base_rules.make:
+## None.
+###########################################################
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := KEYCHARS
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := .bin
+endif
+
+LOCAL_MODULE := $(LOCAL_SRC_FILES)
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+full_src_files := $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES))
+
+$(LOCAL_BUILT_MODULE) : PRIVATE_SRC_FILES := $(full_src_files)
+
+$(LOCAL_BUILT_MODULE) : $(full_src_files) $(KCM)
+ @echo KeyCharMap: $@
+ @mkdir -p $(dir $@)
+ $(hide) $(KCM) $(PRIVATE_SRC_FILES) $@
+
diff --git a/core/main.mk b/core/main.mk
new file mode 100644
index 000000000..1f3412fa5
--- /dev/null
+++ b/core/main.mk
@@ -0,0 +1,656 @@
+
+# Use bash, not whatever shell somebody has installed as /bin/sh
+# This is repeated in config.mk, since envsetup.sh runs that file
+# directly.
+SHELL := /bin/bash
+
+# this turns off the suffix rules built into make
+.SUFFIXES:
+
+# If a rule fails, delete $@.
+.DELETE_ON_ERROR:
+
+# Figure out where we are.
+#TOP := $(dir $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+#TOP := $(patsubst %/,%,$(TOP))
+
+# TOPDIR is the normal variable you should use, because
+# if we are executing relative to the current directory
+# it can be "", whereas TOP must be "." which causes
+# pattern matching probles when make strips off the
+# trailing "./" from paths in various places.
+#ifeq ($(TOP),.)
+#TOPDIR :=
+#else
+#TOPDIR := $(TOP)/
+#endif
+
+# check for broken versions of make
+ifeq (0,$(shell expr $$(echo $(MAKE_VERSION) | sed "s/[^0-9\.].*//") \>= 3.81))
+$(warning ********************************************************************************)
+$(warning * You are using version $(MAKE_VERSION) of make.)
+$(warning * You must upgrade to version 3.81 or greater.)
+$(warning * see file://$(shell pwd)/docs/development-environment/machine-setup.html)
+$(warning ********************************************************************************)
+$(error stopping)
+endif
+
+TOP := .
+TOPDIR :=
+
+BUILD_SYSTEM := $(TOPDIR)build/core
+
+# This is the default target. It must be the first declared target.
+DEFAULT_GOAL := droid
+$(DEFAULT_GOAL):
+
+# Set up various standard variables based on configuration
+# and host information.
+include $(BUILD_SYSTEM)/config.mk
+
+# This allows us to force a clean build - included after the config.make
+# environment setup is done, but before we generate any dependencies. This
+# file does the rm -rf inline so the deps which are all done below will
+# be generated correctly
+include $(BUILD_SYSTEM)/cleanbuild.mk
+
+ifneq ($(HOST_OS),windows)
+ifneq ($(HOST_OS)-$(HOST_ARCH),darwin-ppc)
+# check for a case sensitive file system
+ifneq (a,$(shell mkdir -p $(OUT_DIR) ; \
+ echo a > $(OUT_DIR)/casecheck.txt; \
+ echo B > $(OUT_DIR)/CaseCheck.txt; \
+ cat $(OUT_DIR)/casecheck.txt))
+$(warning ************************************************************)
+$(warning You are building on a case-insensitive filesystem.)
+$(warning Please move your source tree to a case-sensitive filesystem.)
+$(warning ************************************************************)
+$(error Case-insensitive filesystems not supported)
+endif
+endif
+endif
+
+# Make sure that there are no spaces in the absolute path; the
+# build system can't deal with them.
+ifneq ($(words $(shell pwd)),1)
+$(warning ************************************************************)
+$(warning You are building in a directory whose absolute path contains)
+$(warning a space character:)
+$(warning $(space))
+$(warning "$(shell pwd)")
+$(warning $(space))
+$(warning Please move your source tree to a path that does not contain)
+$(warning any spaces.)
+$(warning ************************************************************)
+$(error Directory names containing spaces not supported)
+endif
+
+# Set up version information.
+include $(BUILD_SYSTEM)/version_defaults.mk
+
+# These are the modifier targets that don't do anything themselves, but
+# change the behavior of the build.
+# (must be defined before including definitions.make)
+INTERNAL_MODIFIER_TARGETS := showcommands
+
+# Bring in standard build system definitions.
+include $(BUILD_SYSTEM)/definitions.mk
+
+ifneq ($(filter eng user userdebug tests,$(MAKECMDGOALS)),)
+$(info ***************************************************************)
+$(info ***************************************************************)
+$(info Don't pass '$(filter eng user userdebug tests,$(MAKECMDGOALS))' on \
+ the make command line.)
+$(info Set TARGET_BUILD_VARIANT in buildspec.mk, or use lunch or)
+$(info choosecombo.)
+$(info ***************************************************************)
+$(info ***************************************************************)
+$(error stopping)
+endif
+
+
+###
+### In this section we set up the things that are different
+### between the build variants
+###
+
+## user/userdebug ##
+
+user_variant := $(filter userdebug user,$(TARGET_BUILD_VARIANT))
+enable_target_debugging := true
+ifneq (,$(user_variant))
+ # Target is secure in user builds.
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=1
+
+ override_build_tags := user
+ ifeq ($(user_variant),userdebug)
+ # Pick up some extra useful tools
+ override_build_tags += debug
+ else
+ # Disable debugging in plain user builds.
+ enable_target_debugging :=
+ endif
+
+ # TODO: Always set WITH_DEXPREOPT (for user builds) once it works on OSX.
+ # Also, remove the corresponding block in config/product_config.make.
+ ifeq ($(HOST_OS)-$(WITH_DEXPREOPT_buildbot),linux-true)
+ WITH_DEXPREOPT := true
+ endif
+
+ # Disallow mock locations by default for user builds
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=0
+
+else # !user_variant
+ # Turn on checkjni for non-user builds.
+ ADDITIONAL_BUILD_PROPERTIES += ro.kernel.android.checkjni=1
+ # Set device insecure for non-user builds.
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.secure=0
+ # Allow mock locations by default for non user builds
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.allow.mock.location=1
+endif # !user_variant
+
+ifeq (true,$(strip $(enable_target_debugging)))
+ # Target is more debuggable and adbd is on by default
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.debuggable=1 persist.service.adb.enable=1
+ # Include the debugging/testing OTA keys in this build.
+ INCLUDE_TEST_OTA_KEYS := true
+else # !enable_target_debugging
+ # Target is less debuggable and adbd is off by default
+ ADDITIONAL_DEFAULT_PROPERTIES += ro.debuggable=0 persist.service.adb.enable=0
+endif # !enable_target_debugging
+
+## tests ##
+
+ifeq ($(TARGET_BUILD_VARIANT),tests)
+override_build_tags := eng debug user development tests
+endif
+
+## sdk ##
+
+ifneq ($(filter sdk,$(MAKECMDGOALS)),)
+ifneq ($(words $(filter-out $(INTERNAL_MODIFIER_TARGETS),$(MAKECMDGOALS))),1)
+$(error The 'sdk' target may not be specified with any other targets)
+endif
+override_build_tags := user
+ADDITIONAL_BUILD_PROPERTIES += xmpp.auto-presence=true
+ADDITIONAL_BUILD_PROPERTIES += ro.config.nocheckin=yes
+else # !sdk
+# Enable sync for non-sdk builds only (sdk builds lack SubscribedFeedsProvider).
+ADDITIONAL_BUILD_PROPERTIES += ro.config.sync=yes
+endif
+
+ifeq "" "$(filter %:system/etc/apns-conf.xml, $(PRODUCT_COPY_FILES))"
+ # Install an apns-conf.xml file if one's not already being installed.
+ PRODUCT_COPY_FILES += development/data/etc/apns-conf_sdk.xml:system/etc/apns-conf.xml
+ ifeq ($(filter sdk,$(MAKECMDGOALS)),)
+ $(warning implicitly installing apns-conf_sdk.xml)
+ endif
+endif
+
+ADDITIONAL_BUILD_PROPERTIES += net.bt.name=Android
+
+# enable vm tracing in files for now to help track
+# the cause of ANRs in the content process
+ADDITIONAL_BUILD_PROPERTIES += dalvik.vm.stack-trace-file=/data/anr/traces.txt
+
+
+# ------------------------------------------------------------
+# Define a function that, given a list of module tags, returns
+# non-empty if that module should be installed in /system.
+
+# For most goals, anything tagged with "eng"/"debug"/"user" should
+# be installed in /system.
+define should-install-to-system
+$(filter eng debug user,$(1))
+endef
+
+ifneq (,$(filter sdk,$(MAKECMDGOALS)))
+# For the sdk goal, anything with the "samples" tag should be
+# installed in /data even if that module also has "eng"/"debug"/"user".
+define should-install-to-system
+$(if $(filter samples,$(1)),,$(filter eng debug user development,$(1)))
+endef
+endif
+
+ifeq ($(TARGET_BUILD_VARIANT),)
+# For the default goal, everything should be installed in /system.
+define should-install-to-system
+true
+endef
+endif
+
+
+# If all they typed was make showcommands, we'll actually build
+# the default target.
+ifeq ($(MAKECMDGOALS),showcommands)
+.PHONY: showcommands
+showcommands: $(DEFAULT_GOAL)
+endif
+
+# These targets are going to delete stuff, don't bother including
+# the whole directory tree if that's all we're going to do
+ifeq ($(MAKECMDGOALS),clean)
+dont_bother := true
+endif
+ifeq ($(MAKECMDGOALS),clobber)
+dont_bother := true
+endif
+ifeq ($(MAKECMDGOALS),dataclean)
+dont_bother := true
+endif
+ifeq ($(MAKECMDGOALS),installclean)
+dont_bother := true
+endif
+
+# Bring in all modules that need to be built.
+ifneq ($(dont_bother),true)
+
+subdir_makefiles :=
+
+ifeq ($(HOST_OS),windows)
+SDK_ONLY := true
+endif
+ifeq ($(HOST_OS)-$(HOST_ARCH),darwin-ppc)
+SDK_ONLY := true
+endif
+
+ifeq ($(SDK_ONLY),true)
+
+subdirs := \
+ prebuilt \
+ build/libs/host \
+ dalvik/dexdump \
+ dalvik/libdex \
+ dalvik/tools/dmtracedump \
+ development/emulator/mksdcard \
+ development/tools/activitycreator \
+ development/tools/line_endings \
+ development/host \
+ external/expat \
+ external/libpng \
+ external/qemu \
+ external/sqlite/dist \
+ external/zlib \
+ frameworks/base/libs/utils \
+ frameworks/base/tools/aapt \
+ frameworks/base/tools/aidl \
+ system/core/adb \
+ system/core/fastboot \
+ system/core/libcutils \
+ system/core/liblog \
+ system/core/libzipfile
+
+# The following can only be built if "javac" is available.
+# This check is used when building parts of the SDK under Cygwin.
+ifneq (,$(shell which javac 2>/dev/null))
+$(warning sdk-only: javac available.)
+subdirs += \
+ build/tools/signapk \
+ build/tools/zipalign \
+ dalvik/dx \
+ dalvik/libcore \
+ development/apps \
+ development/tools/androidprefs \
+ development/tools/apkbuilder \
+ development/tools/jarutils \
+ development/tools/layoutlib_utils \
+ development/tools/ninepatch \
+ development/tools/sdkstats \
+ development/tools/sdkmanager \
+ frameworks/base \
+ frameworks/base/tools/layoutlib \
+ external/googleclient \
+ packages
+else
+$(warning sdk-only: javac not available.)
+endif
+
+# Exclude tools/acp when cross-compiling windows under linux
+ifeq ($(findstring Linux,$(UNAME)),)
+subdirs += build/tools/acp
+endif
+
+else # !SDK_ONLY
+ifeq ($(BUILD_TINY_ANDROID), true)
+
+# TINY_ANDROID is a super-minimal build configuration, handy for board
+# bringup and very low level debugging
+
+INTERNAL_DEFAULT_DOCS_TARGETS :=
+
+subdirs := \
+ bionic \
+ system/core \
+ build/libs \
+ build/target \
+ build/tools/acp \
+ build/tools/apriori \
+ build/tools/kcm \
+ build/tools/soslim \
+ external/elfcopy \
+ external/elfutils \
+ external/yaffs2 \
+ external/zlib
+else # !BUILD_TINY_ANDROID
+
+#
+# Typical build; include any Android.mk files we can find.
+#
+INTERNAL_DEFAULT_DOCS_TARGETS := offline-sdk-docs
+subdirs := $(TOP)
+
+FULL_BUILD := true
+
+endif # !BUILD_TINY_ANDROID
+
+endif # !SDK_ONLY
+
+# Can't use first-makefiles-under here because
+# --mindepth=2 makes the prunes not work.
+subdir_makefiles += \
+ $(shell build/tools/findleaves.sh --prune="./out" $(subdirs) Android.mk)
+
+# Boards may be defined under $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)
+# or under vendor/*/$(TARGET_DEVICE). Search in both places, but
+# make sure only one exists.
+# Real boards should always be associated with an OEM vendor.
+board_config_mk := \
+ $(strip $(wildcard \
+ $(SRC_TARGET_DIR)/board/$(TARGET_DEVICE)/BoardConfig.mk \
+ vendor/*/$(TARGET_DEVICE)/BoardConfig.mk \
+ ))
+ifeq ($(board_config_mk),)
+ $(error No config file found for TARGET_DEVICE $(TARGET_DEVICE))
+endif
+ifneq ($(words $(board_config_mk)),1)
+ $(error Multiple board config files for TARGET_DEVICE $(TARGET_DEVICE): $(board_config_mk))
+endif
+include $(board_config_mk)
+TARGET_DEVICE_DIR := $(patsubst %/,%,$(dir $(board_config_mk)))
+board_config_mk :=
+
+# Clean up/verify variables defined by the board config file.
+TARGET_BOOTLOADER_BOARD_NAME := $(strip $(TARGET_BOOTLOADER_BOARD_NAME))
+
+#
+# Include all of the makefiles in the system
+#
+
+ifneq ($(ONE_SHOT_MAKEFILE),)
+# We've probably been invoked by the "mm" shell function
+# with a subdirectory's makefile.
+include $(ONE_SHOT_MAKEFILE)
+# Change CUSTOM_MODULES to include only modules that were
+# defined by this makefile; this will install all of those
+# modules as a side-effect. Do this after including ONE_SHOT_MAKEFILE
+# so that the modules will be installed in the same place they
+# would have been with a normal make.
+CUSTOM_MODULES := $(sort $(call get-tagged-modules,$(ALL_MODULE_TAGS),))
+FULL_BUILD :=
+INTERNAL_DEFAULT_DOCS_TARGETS :=
+# Stub out the notice targets, which probably aren't defined
+# when using ONE_SHOT_MAKEFILE.
+NOTICE-HOST-%: ;
+NOTICE-TARGET-%: ;
+else
+include $(subdir_makefiles)
+endif
+# -------------------------------------------------------------------
+# All module makefiles have been included at this point.
+# -------------------------------------------------------------------
+
+# -------------------------------------------------------------------
+# Include any makefiles that must happen after the module makefiles
+# have been included.
+# TODO: have these files register themselves via a global var rather
+# than hard-coding the list here.
+ifdef FULL_BUILD
+ # Only include this during a full build, otherwise we can't be
+ # guaranteed that any policies were included.
+ -include frameworks/policies/base/PolicyConfig.mk
+endif
+
+# -------------------------------------------------------------------
+# Fix up CUSTOM_MODULES to refer to installed files rather than
+# just bare module names. Leave unknown modules alone in case
+# they're actually full paths to a particular file.
+known_custom_modules := $(filter $(ALL_MODULES),$(CUSTOM_MODULES))
+unknown_custom_modules := $(filter-out $(ALL_MODULES),$(CUSTOM_MODULES))
+CUSTOM_MODULES := \
+ $(call module-installed-files,$(known_custom_modules)) \
+ $(unknown_custom_modules)
+
+# -------------------------------------------------------------------
+# Define dependencies for modules that require other modules.
+# This can only happen now, after we've read in all module makefiles.
+#
+# TODO: deal with the fact that a bare module name isn't
+# unambiguous enough. Maybe declare short targets like
+# APPS:Quake or HOST:SHARED_LIBRARIES:libutils.
+# BUG: the system image won't know to depend on modules that are
+# brought in as requirements of other modules.
+define add-required-deps
+$(1): $(2)
+endef
+$(foreach m,$(ALL_MODULES), \
+ $(eval r := $(ALL_MODULES.$(m).REQUIRED)) \
+ $(if $(r), \
+ $(eval r := $(call module-installed-files,$(r))) \
+ $(eval $(call add-required-deps,$(ALL_MODULES.$(m).INSTALLED),$(r))) \
+ ) \
+ )
+m :=
+r :=
+add-required-deps :=
+
+# -------------------------------------------------------------------
+# Figure out our module sets.
+
+# Of the modules defined by the component makefiles,
+# determine what we actually want to build.
+# If a module has the "restricted" tag on it, it
+# poisons the rest of the tags and shouldn't appear
+# on any list.
+Default_MODULES := $(sort $(ALL_DEFAULT_INSTALLED_MODULES) \
+ $(ALL_BUILT_MODULES) \
+ $(CUSTOM_MODULES))
+
+ifdef FULL_BUILD
+ # The base list of modules to build for this product is specified
+ # by the appropriate product definition file, which was included
+ # by product_config.make.
+ user_PACKAGES := $(call module-installed-files, \
+ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES))
+ ifeq (0,1)
+ $(info user packages for $(TARGET_DEVICE) ($(INTERNAL_PRODUCT)):)
+ $(foreach p,$(user_PACKAGES),$(info : $(p)))
+ $(error done)
+ endif
+else
+ # We're not doing a full build, and are probably only including
+ # a subset of the module makefiles. Don't try to build any modules
+ # requested by the product, because we probably won't have rules
+ # to build them.
+ user_PACKAGES :=
+endif
+# Use tags to get the non-APPS user modules. Use the product
+# definition files to get the APPS user modules.
+user_MODULES := $(sort $(call get-tagged-modules,user,_class@APPS restricted))
+user_MODULES := $(user_MODULES) $(user_PACKAGES)
+
+eng_MODULES := $(sort $(call get-tagged-modules,eng,restricted))
+debug_MODULES := $(sort $(call get-tagged-modules,debug,restricted))
+tests_MODULES := $(sort $(call get-tagged-modules,tests,restricted))
+
+droid_MODULES := $(sort $(Default_MODULES) \
+ $(eng_MODULES) \
+ $(debug_MODULES) \
+ $(user_MODULES) \
+ $(all_development_MODULES))
+
+# THIS IS A TOTAL HACK AND SHOULD NOT BE USED AS AN EXAMPLE
+modules_to_build := $(droid_MODULES)
+ifneq ($(override_build_tags),)
+ modules_to_build := $(sort $(Default_MODULES) \
+ $(foreach tag,$(override_build_tags),$($(tag)_MODULES)))
+#$(error skipping modules $(filter-out $(modules_to_build),$(Default_MODULES) $(droid_MODULES)))
+endif
+
+# Some packages may override others using LOCAL_OVERRIDES_PACKAGES.
+# Filter out (do not install) any overridden packages.
+overridden_packages := $(call get-package-overrides,$(modules_to_build))
+ifdef overridden_packages
+# old_modules_to_build := $(modules_to_build)
+ modules_to_build := \
+ $(filter-out $(foreach p,$(overridden_packages),$(p) %/$(p).apk), \
+ $(modules_to_build))
+endif
+#$(error filtered out $(filter-out $(modules_to_build),$(old_modules_to_build)))
+
+# Don't include any GNU targets in the SDK. It's ok (and necessary)
+# to build the host tools, but nothing that's going to be installed
+# on the target (including static libraries).
+ifneq ($(filter sdk,$(MAKECMDGOALS)),)
+ target_gnu_MODULES := \
+ $(filter \
+ $(TARGET_OUT_INTERMEDIATES)/% \
+ $(TARGET_OUT)/% \
+ $(TARGET_OUT_DATA)/%, \
+ $(sort $(call get-tagged-modules,gnu)))
+ $(info Removing from sdk:)$(foreach d,$(target_gnu_MODULES),$(info : $(d)))
+ modules_to_build := \
+ $(filter-out $(target_gnu_MODULES),$(modules_to_build))
+endif
+
+
+# config/Makefile contains extra stuff that we don't want to pollute this
+# top-level makefile with. It expects that ALL_DEFAULT_INSTALLED_MODULES
+# contains everything that's built during the current make, but it also further
+# extends ALL_DEFAULT_INSTALLED_MODULES.
+ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_build)
+include $(BUILD_SYSTEM)/Makefile
+modules_to_build := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
+ALL_DEFAULT_INSTALLED_MODULES :=
+
+endif # dont_bother
+
+# -------------------------------------------------------------------
+# This is used to to get the ordering right, you can also use these,
+# but they're considered undocumented, so don't complain if their
+# behavior changes.
+.PHONY: prebuilt
+prebuilt: $(ALL_PREBUILT)
+
+# An internal target that depends on all copied headers
+# (see copy_headers.make). Other targets that need the
+# headers to be copied first can depend on this target.
+.PHONY: all_copied_headers
+all_copied_headers: ;
+
+$(ALL_C_CPP_ETC_OBJECTS): | all_copied_headers
+
+# All the droid stuff, in directories
+.PHONY: files
+files: prebuilt $(modules_to_build) $(INSTALLED_ANDROID_INFO_TXT_TARGET)
+
+# -------------------------------------------------------------------
+
+.PHONY: ramdisk
+ramdisk: $(INSTALLED_RAMDISK_TARGET)
+
+.PHONY: systemtarball
+systemtarball: $(INSTALLED_SYSTEMTARBALL_TARGET)
+
+.PHONY: userdataimage
+userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
+
+.PHONY: userdatatarball
+userdatatarball: $(INSTALLED_USERDATATARBALL_TARGET)
+
+.PHONY: bootimage
+bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
+
+ifeq ($(BUILD_TINY_ANDROID), true)
+INSTALLED_RECOVERYIMAGE_TARGET :=
+endif
+
+# Build files and then package it into the rom formats
+.PHONY: droidcore
+droidcore: files \
+ systemimage \
+ $(INSTALLED_BOOTIMAGE_TARGET) \
+ $(INSTALLED_RECOVERYIMAGE_TARGET) \
+ $(INSTALLED_USERDATAIMAGE_TARGET) \
+ $(INTERNAL_DEFAULT_DOCS_TARGETS) \
+ $(INSTALLED_FILES_FILE)
+
+# The actual files built by the droidcore target changes depending
+# on the build variant.
+.PHONY: droid tests
+droid tests: droidcore
+
+$(call dist-for-goals, droid, \
+ $(INTERNAL_UPDATE_PACKAGE_TARGET) \
+ $(INTERNAL_OTA_PACKAGE_TARGET) \
+ $(SYMBOLS_ZIP) \
+ $(APPS_ZIP) \
+ $(INTERNAL_EMULATOR_PACKAGE_TARGET) \
+ $(PACKAGE_STATS_FILE) \
+ $(INSTALLED_FILES_FILE) \
+ $(INSTALLED_BUILD_PROP_TARGET) \
+ $(BUILT_TARGET_FILES_PACKAGE) \
+ )
+
+# Tests are installed in userdata.img. If we're building the tests
+# variant, copy it for "make tests dist". Also copy a zip of the
+# contents of userdata.img, so that people can easily extract a
+# single .apk.
+ifeq ($(TARGET_BUILD_VARIANT),tests)
+$(call dist-for-goals, droid, \
+ $(INSTALLED_USERDATAIMAGE_TARGET) \
+ $(BUILT_TESTS_ZIP_PACKAGE) \
+ )
+endif
+
+.PHONY: docs
+docs: $(ALL_DOCS)
+
+.PHONY: sdk
+ALL_SDK_TARGETS := $(INTERNAL_SDK_TARGET)
+sdk: $(ALL_SDK_TARGETS)
+$(call dist-for-goals,sdk,$(ALL_SDK_TARGETS))
+
+.PHONY: findbugs
+findbugs: $(INTERNAL_FINDBUGS_HTML_TARGET) $(INTERNAL_FINDBUGS_XML_TARGET)
+
+.PHONY: clean
+dirs_to_clean := \
+ $(PRODUCT_OUT) \
+ $(TARGET_COMMON_OUT_ROOT) \
+ $(HOST_OUT) \
+ $(HOST_COMMON_OUT_ROOT)
+clean:
+ @for dir in $(dirs_to_clean) ; do \
+ echo "Cleaning $$dir..."; \
+ rm -rf $$dir; \
+ done
+ @echo "Clean."; \
+
+.PHONY: clobber
+clobber:
+ @rm -rf $(OUT_DIR)
+ @echo "Entire build directory removed."
+
+# The rules for dataclean and installclean are defined in cleanbuild.mk.
+
+#xxx scrape this from ALL_MODULE_NAME_TAGS
+.PHONY: modules
+modules:
+ @echo "Available sub-modules:"
+ @echo "$(call module-names-for-tag-list,$(ALL_MODULE_TAGS))" | \
+ sed -e 's/ */\n/g' | sort -u | $(COLUMN)
+
+.PHONY: showcommands
+showcommands:
+ @echo >/dev/null
+
diff --git a/core/multi_prebuilt.mk b/core/multi_prebuilt.mk
new file mode 100644
index 000000000..707af1057
--- /dev/null
+++ b/core/multi_prebuilt.mk
@@ -0,0 +1,111 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Save these before they get cleared by CLEAR_VARS.
+prebuilt_static_libs := $(filter %.a,$(LOCAL_PREBUILT_LIBS))
+prebuilt_shared_libs := $(filter-out %.a,$(LOCAL_PREBUILT_LIBS))
+prebuilt_executables := $(LOCAL_PREBUILT_EXECUTABLES)
+prebuilt_java_libraries := $(LOCAL_PREBUILT_JAVA_LIBRARIES)
+prebuilt_static_java_libraries := $(LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES)
+prebuilt_is_host := $(LOCAL_IS_HOST_MODULE)
+
+
+ifndef multi_prebuilt_once
+multi_prebuilt_once := true
+
+# $(1): file list
+# $(2): IS_HOST_MODULE
+# $(3): MODULE_CLASS
+# $(4): OVERRIDE_BUILT_MODULE_PATH
+# $(5): UNINSTALLABLE_MODULE
+# $(6): BUILT_MODULE_STEM
+#
+# Elements in the file list may be bare filenames,
+# or of the form "<modulename>:<filename>".
+# If the module name is not specified, the module
+# name will be the filename with the suffix removed.
+#
+define auto-prebuilt-boilerplate
+$(if $(filter %: :%,$(1)), \
+ $(error $(LOCAL_PATH): Leading or trailing colons in "$(1)")) \
+$(foreach t,$(1), \
+ $(eval include $(CLEAR_VARS)) \
+ $(eval LOCAL_IS_HOST_MODULE := $(2)) \
+ $(eval LOCAL_MODULE_CLASS := $(3)) \
+ $(eval OVERRIDE_BUILT_MODULE_PATH := $(4)) \
+ $(eval LOCAL_UNINSTALLABLE_MODULE := $(5)) \
+ $(eval tw := $(subst :, ,$(strip $(t)))) \
+ $(if $(word 3,$(tw)),$(error $(LOCAL_PATH): Bad prebuilt filename '$(t)')) \
+ $(if $(word 2,$(tw)), \
+ $(eval LOCAL_MODULE := $(word 1,$(tw))) \
+ $(eval LOCAL_SRC_FILES := $(word 2,$(tw))) \
+ , \
+ $(eval LOCAL_MODULE := $(basename $(t))) \
+ $(eval LOCAL_SRC_FILES := $(t)) \
+ ) \
+ $(if $(6), \
+ $(eval LOCAL_BUILT_MODULE_STEM := $(6)) \
+ , \
+ $(eval LOCAL_BUILT_MODULE_STEM := $(LOCAL_SRC_FILES)) \
+ ) \
+ $(eval LOCAL_MODULE_SUFFIX := $(suffix $(LOCAL_SRC_FILES))) \
+ $(eval include $(BUILD_PREBUILT)) \
+ )
+endef
+
+endif # multi_prebuilt_once
+
+
+$(call auto-prebuilt-boilerplate, \
+ $(prebuilt_static_libs), \
+ $(prebuilt_is_host), \
+ STATIC_LIBRARIES, \
+ , \
+ true)
+
+$(call auto-prebuilt-boilerplate, \
+ $(prebuilt_shared_libs), \
+ $(prebuilt_is_host), \
+ SHARED_LIBRARIES, \
+ $($(if $(prebuilt_is_host),HOST,TARGET)_OUT_INTERMEDIATE_LIBRARIES))
+
+$(call auto-prebuilt-boilerplate, \
+ $(prebuilt_executables), \
+ $(prebuilt_is_host), \
+ EXECUTABLES)
+
+$(call auto-prebuilt-boilerplate, \
+ $(prebuilt_java_libraries), \
+ $(prebuilt_is_host), \
+ JAVA_LIBRARIES, \
+ , \
+ , \
+ javalib.jar)
+
+$(call auto-prebuilt-boilerplate, \
+ $(prebuilt_static_java_libraries), \
+ $(prebuilt_is_host), \
+ JAVA_LIBRARIES, \
+ , \
+ true, \
+ javalib.jar)
+
+prebuilt_static_libs :=
+prebuilt_shared_libs :=
+prebuilt_executables :=
+prebuilt_java_libraries :=
+prebuilt_static_java_libraries :=
+prebuilt_is_host :=
diff --git a/core/node_fns.mk b/core/node_fns.mk
new file mode 100644
index 000000000..202bb0d86
--- /dev/null
+++ b/core/node_fns.mk
@@ -0,0 +1,238 @@
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Clears a list of variables using ":=".
+#
+# E.g.,
+# $(call clear-var-list,A B C)
+# would be the same as:
+# A :=
+# B :=
+# C :=
+#
+# $(1): list of variable names to clear
+#
+define clear-var-list
+$(foreach v,$(1),$(eval $(v):=))
+endef
+
+#
+# Copies a list of variables into another list of variables.
+# The target list is the same as the source list, but has
+# a dotted prefix affixed to it.
+#
+# E.g.,
+# $(call copy-var-list, PREFIX, A B)
+# would be the same as:
+# PREFIX.A := $(A)
+# PREFIX.B := $(B)
+#
+# $(1): destination prefix
+# $(2): list of variable names to copy
+#
+define copy-var-list
+$(foreach v,$(2),$(eval $(strip $(1)).$(v):=$($(v))))
+endef
+
+#
+# Moves a list of variables into another list of variables.
+# The variable names differ by a prefix. After moving, the
+# source variable is cleared.
+#
+# NOTE: Spaces are not allowed around the prefixes.
+#
+# E.g.,
+# $(call move-var-list,SRC,DST,A B)
+# would be the same as:
+# DST.A := $(SRC.A)
+# SRC.A :=
+# DST.B := $(SRC.B)
+# SRC.B :=
+#
+# $(1): source prefix
+# $(2): destination prefix
+# $(3): list of variable names to move
+#
+define move-var-list
+$(foreach v,$(3), \
+ $(eval $(2).$(v) := $($(1).$(v))) \
+ $(eval $(1).$(v) :=) \
+ )
+endef
+
+#
+# $(1): haystack
+# $(2): needle
+#
+# Guarantees that needle appears at most once in haystack,
+# without changing the order of other elements in haystack.
+# If needle appears multiple times, only the first occurrance
+# will survive.
+#
+# How it works:
+#
+# - Stick everything in haystack into a single word,
+# with "|||" separating the words.
+# - Replace occurrances of "|||$(needle)|||" with "||| |||",
+# breaking haystack back into multiple words, with spaces
+# where needle appeared.
+# - Add needle between the first and second words of haystack.
+# - Replace "|||" with spaces, breaking haystack back into
+# individual words.
+#
+empty :=
+space := $(empty) $(empty)
+define uniq-word
+$(strip \
+ $(if $(filter $(2),$(1)), \
+ $(eval h := |||$(subst $(space),|||,$(strip $(1)))|||) \
+ $(eval h := $(subst |||$(strip $(2))|||,|||$(space)|||,$(h))) \
+ $(eval h := $(word 1,$(h)) $(2) $(wordlist 2,9999,$(h))) \
+ $(subst |||,$(space),$(h)) \
+ , \
+ $(1) \
+ ))
+endef
+
+INHERIT_TAG := @inherit:
+
+#
+# Walks through the list of variables, each qualified by the prefix,
+# and finds instances of words beginning with INHERIT_TAG. Scrape
+# off INHERIT_TAG from each matching word, and return the sorted,
+# unique set of those words.
+#
+# E.g., given
+# PREFIX.A := A $(INHERIT_TAG)aaa B C
+# PREFIX.B := B $(INHERIT_TAG)aaa C $(INHERIT_TAG)bbb D E
+# Then
+# $(call get-inherited-nodes,PREFIX,A B)
+# returns
+# aaa bbb
+#
+# $(1): variable prefix
+# $(2): list of variables to check
+#
+define get-inherited-nodes
+$(sort \
+ $(subst $(INHERIT_TAG),, \
+ $(filter $(INHERIT_TAG)%, \
+ $(foreach v,$(2),$($(1).$(v))) \
+ )))
+endef
+
+#
+# for each variable ( (prefix + name) * vars ):
+# get list of inherited words; if not empty:
+# for each inherit:
+# replace the first occurrence with (prefix + inherited + var)
+# clear the source var so we can't inherit the value twice
+#
+# $(1): context prefix
+# $(2): name of this node
+# $(3): list of variable names
+#
+define _expand-inherited-values
+ $(foreach v,$(3), \
+ $(eval ### "Shorthand for the name of the target variable") \
+ $(eval _eiv_tv := $(1).$(2).$(v)) \
+ $(eval ### "Get the list of nodes that this variable inherits") \
+ $(eval _eiv_i := \
+ $(sort \
+ $(patsubst $(INHERIT_TAG)%,%, \
+ $(filter $(INHERIT_TAG)%, $($(_eiv_tv)) \
+ )))) \
+ $(foreach i,$(_eiv_i), \
+ $(eval ### "Make sure that this inherit appears only once") \
+ $(eval $(_eiv_tv) := \
+ $(call uniq-word,$($(_eiv_tv)),$(INHERIT_TAG)$(i))) \
+ $(eval ### "Expand the inherit tag") \
+ $(eval $(_eiv_tv) := \
+ $(patsubst $(INHERIT_TAG)$(i),$($(1).$(i).$(v)), \
+ $($(_eiv_tv)))) \
+ $(eval ### "Clear the child so DAGs don't create duplicate entries" ) \
+ $(eval $(1).$(i).$(v) :=) \
+ $(eval ### "If we just inherited ourselves, it's a cycle.") \
+ $(if $(filter $(INHERIT_TAG)$(2),$($(_eiv_tv))), \
+ $(warning Cycle detected between "$(2)" and "$(i)" for context "$(1)") \
+ $(error import of "$(2)" failed) \
+ ) \
+ ) \
+ ) \
+ $(eval _eiv_tv :=) \
+ $(eval _eiv_i :=)
+endef
+
+#
+# $(1): context prefix
+# $(2): makefile representing this node
+# $(3): list of node variable names
+#
+#TODO: keep a debug stack to make error messages more helpful
+define _import-node
+ $(call clear-var-list, $(3))
+ $(eval include $(2))
+ $(call copy-var-list, $(1).$(2), $(3))
+ $(call clear-var-list, $(3))
+
+ $(eval $(1).$(2).inherited := \
+ $(call get-inherited-nodes,$(1).$(2),$(3)))
+ $(call _import-nodes-inner,$(1),$($(1).$(2).inherited),$(3))
+
+ $(call _expand-inherited-values,$(1),$(2),$(3))
+
+ $(eval $(1).$(2).inherited :=)
+endef
+
+#
+# $(1): context prefix
+# $(2): list of makefiles representing nodes to import
+# $(3): list of node variable names
+#
+#TODO: Make the "does not exist" message more helpful;
+# should print out the name of the file trying to include it.
+define _import-nodes-inner
+ $(foreach _in,$(2), \
+ $(if $(wildcard $(_in)), \
+ $(if $($(1).$(_in).seen), \
+ $(eval ### "skipping already-imported $(_in)") \
+ , \
+ $(eval $(1).$(_in).seen := true) \
+ $(call _import-node,$(1),$(strip $(_in)),$(3)) \
+ ) \
+ , \
+ $(error $(1): "$(_in)" does not exist) \
+ ) \
+ )
+endef
+
+#
+# $(1): output list variable name, like "PRODUCTS" or "DEVICES"
+# $(2): list of makefiles representing nodes to import
+# $(3): list of node variable names
+#
+define import-nodes
+$(if \
+ $(foreach _in,$(2), \
+ $(eval _node_import_context := _nic.$(1).[[$(_in)]]) \
+ $(call _import-nodes-inner,$(_node_import_context),$(_in),$(3)) \
+ $(call move-var-list,$(_node_import_context).$(_in),$(1).$(_in),$(3)) \
+ $(eval _node_import_context :=) \
+ $(eval $(1) := $($(1)) $(_in)) \
+ ) \
+,)
+endef
diff --git a/core/notice_files.mk b/core/notice_files.mk
new file mode 100644
index 000000000..24295c781
--- /dev/null
+++ b/core/notice_files.mk
@@ -0,0 +1,65 @@
+###########################################################
+## Track NOTICE files
+###########################################################
+
+notice_file:=$(shell find $(LOCAL_PATH) -maxdepth 1 -name NOTICE)
+
+ifneq ($(strip $(notice_file)),)
+
+# This relies on the name of the directory in PRODUCT_OUT matching where
+# it's installed on the target - i.e. system, data, etc. This does
+# not work for root and isn't exact, but it's probably good enough for
+# compliance.
+# Includes the leading slash
+ifdef LOCAL_INSTALLED_MODULE
+ module_installed_filename := $(patsubst $(PRODUCT_OUT)%,%,$(LOCAL_INSTALLED_MODULE))
+else
+ # This module isn't installable
+ ifeq ($(LOCAL_MODULE_CLASS),STATIC_LIBRARIES)
+ # Stick the static libraries with the dynamic libraries.
+ # We can't use xxx_OUT_STATIC_LIBRARIES because it points into
+ # device-obj or host-obj.
+ module_installed_filename := \
+ $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_SHARED_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+ else
+ ifeq ($(LOCAL_MODULE_CLASS),JAVA_LIBRARIES)
+ # Stick the static java libraries with the regular java libraries.
+ module_installed_filename := \
+ $(patsubst $(PRODUCT_OUT)%,%,$($(my_prefix)OUT_JAVA_LIBRARIES))/$(notdir $(LOCAL_BUILT_MODULE))
+ else
+ $(error Cannot determine where to install NOTICE file for $(LOCAL_MODULE))
+ endif # JAVA_LIBRARIES
+ endif # STATIC_LIBRARIES
+endif
+
+# In case it's actually a host file
+module_installed_filename := $(patsubst $(HOST_OUT)%,%,$(module_installed_filename))
+
+installed_notice_file := $($(my_prefix)OUT_NOTICE_FILES)/src/$(module_installed_filename).txt
+
+$(installed_notice_file): PRIVATE_INSTALLED_MODULE := $(module_installed_filename)
+
+$(installed_notice_file): $(notice_file)
+ @echo Notice file: $< -- $@
+ $(hide) mkdir -p $(dir $@)
+ $(hide) cat $< >> $@
+
+ifdef LOCAL_INSTALLED_MODULE
+# Make LOCAL_INSTALLED_MODULE depend on NOTICE files if they exist
+# libraries so they get installed along with it. Make it an order-only
+# dependency so we don't re-install a module when the NOTICE changes.
+$(LOCAL_INSTALLED_MODULE): | $(installed_notice_file)
+endif
+
+else
+# NOTICE file does not exist
+installed_notice_file :=
+endif
+
+# Create a predictable, phony target to build this notice file.
+# Define it even if the notice file doesn't exist so that other
+# modules can depend on it.
+notice_target := NOTICE-$(if \
+ $(LOCAL_IS_HOST_MODULE),HOST,TARGET)-$(LOCAL_MODULE_CLASS)-$(LOCAL_MODULE)
+.PHONY: $(notice_target)
+$(notice_target): $(installed_notice_file)
diff --git a/core/package.mk b/core/package.mk
new file mode 100644
index 000000000..a21255339
--- /dev/null
+++ b/core/package.mk
@@ -0,0 +1,273 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+###########################################################
+## Standard rules for building an application package.
+##
+## Additional inputs from base_rules.make:
+## LOCAL_PACKAGE_NAME: The name of the package; the directory
+## will be called this.
+##
+## MODULE, MODULE_PATH, and MODULE_SUFFIX will
+## be set for you.
+###########################################################
+
+LOCAL_PACKAGE_NAME := $(strip $(LOCAL_PACKAGE_NAME))
+ifeq ($(LOCAL_PACKAGE_NAME),)
+$(error $(LOCAL_PATH): Package modules must define LOCAL_PACKAGE_NAME)
+endif
+
+LOCAL_MODULE_TAGS := $(strip $(LOCAL_MODULE_TAGS))
+ifeq ($(LOCAL_MODULE_TAGS),)
+$(error $(LOCAL_PATH): Package modules must define LOCAL_MODULE_TAGS)
+endif
+
+#$(warning $(LOCAL_PATH) $(LOCAL_PACKAGE_NAME) $(sort $(LOCAL_MODULE_TAGS)))
+
+ifneq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE_SUFFIX)
+endif
+LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)
+
+ifneq ($(strip $(LOCAL_MODULE)),)
+$(error $(LOCAL_PATH): Package modules may not define LOCAL_MODULE)
+endif
+LOCAL_MODULE := $(LOCAL_PACKAGE_NAME)
+
+# Android packages should use Android resources or assets.
+ifneq (,$(LOCAL_JAVA_RESOURCE_DIRS))
+$(error $(LOCAL_PATH): Package modules may not set LOCAL_JAVA_RESOURCE_DIRS)
+endif
+ifneq (,$(LOCAL_JAVA_RESOURCE_FILES))
+$(error $(LOCAL_PATH): Package modules may not set LOCAL_JAVA_RESOURCE_FILES)
+endif
+
+ifneq ($(strip $(LOCAL_MODULE_CLASS)),)
+$(error $(LOCAL_PATH): Package modules may not set LOCAL_MODULE_CLASS)
+endif
+LOCAL_MODULE_CLASS := APPS
+
+ifeq (,$(LOCAL_ASSET_DIR))
+LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
+endif
+
+ifeq (,$(LOCAL_RESOURCE_DIR))
+ LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
+endif
+LOCAL_RESOURCE_DIR := \
+ $(wildcard $(addsuffix /$(LOCAL_RESOURCE_DIR), $(PRODUCT_PACKAGE_OVERLAYS))) \
+ $(wildcard $(addsuffix /$(LOCAL_RESOURCE_DIR), $(DEVICE_PACKAGE_OVERLAYS))) \
+ $(LOCAL_RESOURCE_DIR)
+
+# this is an app, so add the system libraries to the search path
+LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
+
+#$(warning Finding assets for $(LOCAL_ASSET_DIR))
+
+all_assets := $(call find-subdir-assets,$(LOCAL_ASSET_DIR))
+all_assets := $(addprefix $(LOCAL_ASSET_DIR)/,$(patsubst assets/%,%,$(all_assets)))
+
+all_resources := $(strip \
+ $(foreach dir, $(LOCAL_RESOURCE_DIR), \
+ $(addprefix $(dir)/, \
+ $(patsubst res/%,%, \
+ $(call find-subdir-assets,$(dir)) \
+ ) \
+ ) \
+ ))
+
+all_res_assets := $(strip $(all_assets) $(all_resources))
+
+# If no assets or resources were found, clear the directory variables so
+# we don't try to build them.
+ifeq (,$(all_assets))
+LOCAL_ASSET_DIR:=
+endif
+ifeq (,$(all_resources))
+LOCAL_RESOURCE_DIR:=
+R_file_stamp :=
+else
+# Make sure that R_file_stamp inherits the proper PRIVATE vars.
+# If R.stamp moves, be sure to update the framework makefile,
+# which has intimate knowledge of its location.
+package_expected_intermediates_COMMON := $(call local-intermediates-dir,COMMON)
+R_file_stamp := $(package_expected_intermediates_COMMON)/src/R.stamp
+LOCAL_INTERMEDIATE_TARGETS += $(R_file_stamp)
+endif
+
+LOCAL_BUILT_MODULE_STEM := package.apk
+
+# The dex files go in the package, so we don't
+# want to install them separately for this module.
+old_DONT_INSTALL_DEX_FILES := $(DONT_INSTALL_DEX_FILES)
+DONT_INSTALL_DEX_FILES := true
+#################################
+include $(BUILD_SYSTEM)/java.mk
+#################################
+DONT_INSTALL_DEX_FILES := $(old_DONT_INSTALL_DEX_FILES)
+old_DONT_INSTALL_DEX_FILES =
+
+full_android_manifest := $(LOCAL_PATH)/AndroidManifest.xml
+$(LOCAL_INTERMEDIATE_TARGETS): \
+ PRIVATE_ANDROID_MANIFEST := $(full_android_manifest)
+
+ifneq ($(all_resources),)
+
+# Since we don't know where the real R.java file is going to end up,
+# we need to use another file to stand in its place. We'll just
+# copy the generated file to src/R.stamp, which means it will
+# have the same contents and timestamp as the actual file.
+#
+# At the same time, this will copy the R.java file to a central
+# 'R' directory to make it easier to add the files to an IDE.
+#
+#TODO: use PRIVATE_SOURCE_INTERMEDIATES_DIR instead of
+# $(intermediates.COMMON)/src
+ifneq ($(package_expected_intermediates_COMMON),$(intermediates.COMMON))
+ $(error $(LOCAL_MODULE): internal error: expected intermediates.COMMON "$(package_expected_intermediates_COMMON)" != intermediates.COMMON "$(intermediates.COMMON)")
+endif
+
+$(R_file_stamp): PRIVATE_RESOURCE_PUBLICS_OUTPUT := \
+ $(intermediates.COMMON)/public_resources.xml
+$(R_file_stamp): $(all_res_assets) $(full_android_manifest) $(AAPT) | $(ACP)
+ @echo "target R.java/Manifest.java: $(PRIVATE_MODULE) ($@)"
+ @rm -f $@
+ $(create-resource-java-files)
+ $(hide) for GENERATED_MANIFEST_FILE in `find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) \
+ -name Manifest.java 2> /dev/null`; do \
+ dir=`grep package $$GENERATED_MANIFEST_FILE | head -n1 | \
+ awk '{print $$2}' | tr -d ";" | tr . /`; \
+ mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
+ $(ACP) -fpt $$GENERATED_MANIFEST_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
+ done;
+ $(hide) for GENERATED_R_FILE in `find $(PRIVATE_SOURCE_INTERMEDIATES_DIR) \
+ -name R.java 2> /dev/null`; do \
+ dir=`grep package $$GENERATED_R_FILE | head -n1 | \
+ awk '{print $$2}' | tr -d ";" | tr . /`; \
+ mkdir -p $(TARGET_COMMON_OUT_ROOT)/R/$$dir; \
+ $(ACP) -fpt $$GENERATED_R_FILE $(TARGET_COMMON_OUT_ROOT)/R/$$dir \
+ || exit 31; \
+ $(ACP) -fpt $$GENERATED_R_FILE $@ || exit 32; \
+ done; \
+
+ifdef LOCAL_EXPORT_PACKAGE_RESOURCES
+# Put this module's resources into a PRODUCT-agnositc package that
+# other packages can use to build their own PRODUCT-agnostic R.java (etc.)
+# files.
+resource_export_package := $(intermediates.COMMON)/package-export.apk
+$(R_file_stamp): $(resource_export_package)
+
+# add-assets-to-package looks at PRODUCT_AAPT_CONFIG, but this target
+# can't know anything about PRODUCT. Clear it out just for this target.
+$(resource_export_package): PRODUCT_AAPT_CONFIG :=
+$(resource_export_package): $(all_res_assets) $(full_android_manifest) $(AAPT)
+ @echo "target Export Resources: $(PRIVATE_MODULE) ($@)"
+ $(create-empty-package)
+ $(add-assets-to-package)
+endif
+
+# Other modules should depend on the BUILT module if
+# they want to use this module's R.java file.
+$(LOCAL_BUILT_MODULE): $(R_file_stamp)
+
+ifneq ($(full_classes_jar),)
+# If full_classes_jar is non-empty, we're building sources.
+# If we're building sources, the initial javac step (which
+# produces full_classes_compiled_jar) needs to ensure the
+# R.java and Manifest.java files have been generated first.
+$(full_classes_compiled_jar): $(R_file_stamp)
+endif
+
+endif # all_resources
+
+ifeq ($(LOCAL_NO_STANDARD_LIBRARIES),true)
+# We need to explicitly clear this var so that we don't
+# inherit the value from whomever caused us to be built.
+$(LOCAL_INTERMEDIATE_TARGETS): PRIVATE_AAPT_INCLUDES :=
+else
+# Most packages should link against the resources defined by framework-res.
+# Even if they don't have their own resources, they may use framework
+# resources.
+framework_res_package_export := \
+ $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk
+$(LOCAL_INTERMEDIATE_TARGETS): \
+ PRIVATE_AAPT_INCLUDES := $(framework_res_package_export)
+# We can't depend directly on the export.apk file; it won't get its
+# PRIVATE_ vars set up correctly if we do. Instead, depend on the
+# corresponding R.stamp file, which lists the export.apk as a dependency.
+framework_res_package_export_deps := \
+ $(dir $(framework_res_package_export))src/R.stamp
+$(R_file_stamp): $(framework_res_package_export_deps)
+endif
+
+ifneq ($(full_classes_jar),)
+$(LOCAL_BUILT_MODULE): PRIVATE_DEX_FILE := $(built_dex)
+$(LOCAL_BUILT_MODULE): $(built_dex)
+endif # full_classes_jar
+
+
+# Get the list of jni libraries to be included in the apk file.
+
+so_suffix := $($(my_prefix)SHLIB_SUFFIX)
+
+jni_shared_libraries := \
+ $(addprefix $($(my_prefix)OUT_INTERMEDIATE_LIBRARIES)/, \
+ $(addsuffix $(so_suffix), \
+ $(LOCAL_JNI_SHARED_LIBRARIES)))
+
+# Pick a key to sign the package with. If this package hasn't specified
+# an explicit certificate, use the default.
+# Secure release builds will have their packages signed after the fact,
+# so it's ok for these private keys to be in the clear.
+ifeq ($(LOCAL_CERTIFICATE),)
+ LOCAL_CERTIFICATE := testkey
+endif
+# If this is not an absolute certificate, assign it to a generic one.
+ifeq ($(dir $(strip $(LOCAL_CERTIFICATE))),./)
+ LOCAL_CERTIFICATE := $(SRC_TARGET_DIR)/product/security/$(LOCAL_CERTIFICATE)
+endif
+private_key := $(LOCAL_CERTIFICATE).pk8
+certificate := $(LOCAL_CERTIFICATE).x509.pem
+
+$(LOCAL_BUILT_MODULE): $(private_key) $(certificate) $(SIGNAPK_JAR)
+$(LOCAL_BUILT_MODULE): PRIVATE_PRIVATE_KEY := $(private_key)
+$(LOCAL_BUILT_MODULE): PRIVATE_CERTIFICATE := $(certificate)
+
+PACKAGES.$(LOCAL_PACKAGE_NAME).PRIVATE_KEY := $(private_key)
+PACKAGES.$(LOCAL_PACKAGE_NAME).CERTIFICATE := $(certificate)
+
+# Define the rule to build the actual package.
+$(LOCAL_BUILT_MODULE): $(AAPT) | $(ZIPALIGN)
+$(LOCAL_BUILT_MODULE): PRIVATE_JNI_SHARED_LIBRARIES := $(jni_shared_libraries)
+$(LOCAL_BUILT_MODULE): $(all_res_assets) $(jni_shared_libraries) $(full_android_manifest)
+ @echo "target Package: $(PRIVATE_MODULE) ($@)"
+ $(create-empty-package)
+ $(add-assets-to-package)
+ifneq ($(jni_shared_libraries),)
+ $(add-jni-shared-libs-to-package)
+endif
+ifneq ($(full_classes_jar),)
+ $(add-dex-to-package)
+endif
+ $(sign-package)
+ @# Alignment must happen after all other zip operations.
+ $(align-package)
+
+# Save information about this package
+PACKAGES.$(LOCAL_PACKAGE_NAME).OVERRIDES := $(strip $(LOCAL_OVERRIDES_PACKAGES))
+PACKAGES.$(LOCAL_PACKAGE_NAME).RESOURCE_FILES := $(all_resources)
+
+PACKAGES := $(PACKAGES) $(LOCAL_PACKAGE_NAME)
diff --git a/core/pathmap.mk b/core/pathmap.mk
new file mode 100644
index 000000000..13cb80d04
--- /dev/null
+++ b/core/pathmap.mk
@@ -0,0 +1,95 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# A central place to define mappings to paths, to avoid hard-coding
+# them in Android.mk files.
+#
+# TODO: Allow each project to define stuff like this before the per-module
+# Android.mk files are included, so we don't need to have a big central
+# list.
+#
+
+#
+# A mapping from shorthand names to include directories.
+#
+pathmap_INCL := \
+ bluedroid:system/bluetooth/bluedroid/include \
+ bluez-libs:external/bluez/libs/include \
+ bluez-utils:external/bluez/utils \
+ bootloader:bootable/bootloader/legacy/include \
+ corecg:external/skia/include/core \
+ dbus:external/dbus \
+ frameworks-base:frameworks/base/include \
+ graphics:external/skia/include/core \
+ libc:bionic/libc/include \
+ libdrm1:frameworks/base/media/libdrm/mobile1/include \
+ libdrm2:frameworks/base/media/libdrm/mobile2/include \
+ libhardware:hardware/libhardware/include \
+ libhardware_legacy:hardware/libhardware_legacy/include \
+ libhost:build/libs/host/include \
+ libm:bionic/libm/include \
+ libnativehelper:dalvik/libnativehelper/include \
+ libpagemap:system/extras/libpagemap/include \
+ libril:hardware/ril/include \
+ libstdc++:bionic/libstdc++/include \
+ libthread_db:bionic/libthread_db/include \
+ mkbootimg:system/core/mkbootimg \
+ recovery:bootable/recovery \
+ system-core:system/core/include
+
+#
+# Returns the path to the requested module's include directory,
+# relative to the root of the source tree. Does not handle external
+# modules.
+#
+# $(1): a list of modules (or other named entities) to find the includes for
+#
+define include-path-for
+$(foreach n,$(1),$(patsubst $(n):%,%,$(filter $(n):%,$(pathmap_INCL))))
+endef
+
+#
+# Many modules expect to be able to say "#include <jni.h>",
+# so make it easy for them to find the correct path.
+#
+JNI_H_INCLUDE := $(call include-path-for,libnativehelper)/nativehelper
+
+#
+# A list of all source roots under frameworks/base, which will be
+# built into the android.jar.
+#
+FRAMEWORKS_BASE_SUBDIRS := \
+ $(addsuffix /java, \
+ core \
+ graphics \
+ im \
+ location \
+ media \
+ opengl \
+ sax \
+ telephony \
+ wifi \
+ )
+
+#
+# A version of FRAMEWORKS_BASE_SUBDIRS that is expanded to full paths from
+# the root of the tree. This currently needs to be here so that other libraries
+# and apps can find the .aidl files in the framework, though we should really
+# figure out a better way to do this.
+#
+FRAMEWORKS_BASE_JAVA_SRC_DIRS := \
+ $(addprefix frameworks/base/,$(FRAMEWORKS_BASE_SUBDIRS))
diff --git a/core/prebuilt.mk b/core/prebuilt.mk
new file mode 100644
index 000000000..2d931624f
--- /dev/null
+++ b/core/prebuilt.mk
@@ -0,0 +1,37 @@
+###########################################################
+## Standard rules for copying files that are prebuilt
+##
+## Additional inputs from base_rules.make:
+## None.
+##
+###########################################################
+
+ifneq ($(LOCAL_PREBUILT_LIBS),)
+$(error dont use LOCAL_PREBUILT_LIBS anymore LOCAL_PATH=$(LOCAL_PATH))
+endif
+ifneq ($(LOCAL_PREBUILT_EXECUTABLES),)
+$(error dont use LOCAL_PREBUILT_EXECUTABLES anymore LOCAL_PATH=$(LOCAL_PATH))
+endif
+ifneq ($(LOCAL_PREBUILT_JAVA_LIBRARIES),)
+$(error dont use LOCAL_PREBUILT_JAVA_LIBRARIES anymore LOCAL_PATH=$(LOCAL_PATH))
+endif
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+# Deal with the OSX library timestamp issue when installing
+# a prebuilt simulator library.
+ifneq ($(filter STATIC_LIBRARIES SHARED_LIBRARIES,$(LOCAL_MODULE_CLASS)),)
+ prebuilt_module_is_a_library := true
+else
+ prebuilt_module_is_a_library :=
+endif
+
+$(LOCAL_BUILT_MODULE) : $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ACP)
+ $(transform-prebuilt-to-target)
+ifneq ($(prebuilt_module_is_a_library),)
+ ifneq ($(LOCAL_IS_HOST_MODULE),)
+ $(transform-host-ranlib-copy-hack)
+ else
+ $(transform-ranlib-copy-hack)
+ endif
+endif
diff --git a/core/prelink-linux-arm.map b/core/prelink-linux-arm.map
new file mode 100644
index 000000000..429ae1a1c
--- /dev/null
+++ b/core/prelink-linux-arm.map
@@ -0,0 +1,139 @@
+
+# 0xC0000000 - 0xFFFFFFFF Kernel
+# 0xB0100000 - 0xBFFFFFFF Thread 0 Stack
+# 0xB0000000 - 0xB00FFFFF Linker
+# 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
+# 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
+# 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
+# 0x40000000 - 0x7FFFFFFF mmap'd stuff
+# 0x10000000 - 0x3FFFFFFF Thread Stacks
+# 0x00000000 - 0x0FFFFFFF .text / .data / heap
+
+# core system libraries
+libdl.so 0xAFF00000
+libc.so 0xAFE00000
+libstdc++.so 0xAFD00000
+libm.so 0xAFC00000
+liblog.so 0xAFBC0000
+libcutils.so 0xAFB00000
+libthread_db.so 0xAFA00000
+libz.so 0xAF900000
+libevent.so 0xAF800000
+libssl.so 0xAF700000
+libcrypto.so 0xAF500000
+
+# bluetooth
+liba2dp.so 0xAEE00000
+audio.so 0xAED00000
+input.so 0xAEC00000
+libhcid.so 0xAEB00000
+libbluedroid.so 0xAEA00000
+libbluetooth.so 0xAE900000
+libdbus.so 0xAE800000
+
+# extended system libraries
+libril.so 0xAE400000
+libreference-ril.so 0xAE000000
+libwpa_client.so 0xADC00000
+libnetutils.so 0xADB00000
+
+# core dalvik runtime support
+libandroid_servers.so 0xAD900000
+libicudata.so 0xAD600000
+libicuuc.so 0xAD500000
+libicui18n.so 0xAD400000
+libandroid_runtime.so 0xAD300000
+libnativehelper.so 0xAD200000
+libdvm-ARM.so 0xAD100000
+libdvm.so 0xAD000000
+
+# graphics
+libpixelflinger.so 0xACF00000
+libcorecg.so 0xACE00000
+libsurfaceflinger.so 0xACD00000
+libagl.so 0xACC00000
+
+libGLESv1_CM.so 0xACB00000
+libGLESv2.so 0xACA00000
+libOpenVG_CM.so 0xAC900000
+libOpenVGU_CM.so 0xAC800000
+libEGL.so 0xAC700000
+
+libexif.so 0xAC500000
+libui.so 0xAC400000
+libsgl.so 0xAC000000
+
+# audio
+libspeech.so 0xAB800000
+libaudio.so 0xAB700000
+libsonivox.so 0xAB600000
+libsoundpool.so 0xAB500000
+libvorbisidec.so 0xAB400000
+libmedia_jni.so 0xAB300000
+libmediaplayerservice.so 0xAB280000
+libmedia.so 0xAB200000
+libFFTEm.so 0xAB100000
+libaudioflinger.so 0xAB000000
+
+# assorted system libraries
+libsqlite.so 0xAAC00000
+libexpat.so 0xAAB00000
+libwebcore.so 0xAA000000
+libutils.so 0xA9D00000
+libcameraservice.so 0xA9C80000
+libhardware.so 0xA9C70000
+libhardware_legacy.so 0xA9C00000
+libapp_process.so 0xA9B00000
+libsystem_server.so 0xA9A00000
+libime.so 0xA9800000
+libgps.so 0xA9700000
+libcamera.so 0xA9680000
+libqcamera.so 0xA9400000
+
+# pv libraries
+libopencorenet_support.so 0xA7D20000
+libpvasf.so 0xA7BC0000
+libpvasfreg.so 0xA7B70000
+libopencoredownload.so 0xA7B40000
+libopencoredownloadreg.so 0xA7B00000
+libopencorenet_support.so 0xA7A00000
+libopencorertsp.so 0xA7900000
+libopencorertspreg.so 0xA7800000
+libopencoreauthor.so 0xA7600000
+libopencorecommon.so 0xA7500000
+libopencoremp4.so 0xA7400000
+libopencoremp4reg.so 0xA7300000
+libopencoreplayer.so 0xA7000000
+
+# opencore hardware support
+libmm-adspsvc.so 0xA6FFD000
+libOmxCore.so 0xA6FF0000
+libOmxMpeg4Dec.so 0xA6FC0000
+libOmxH264Dec.so 0xA6F90000
+libOmxVidEnc.so 0xA6F60000
+libopencorehw.so 0xA6F50000
+
+# libraries for specific apps or temporary libraries
+libcam_ipl.so 0x9F000000
+libwbxml.so 0x9E800000
+libwbxml_jni.so 0x9E400000
+libxml2wbxml.so 0x9E000000
+libaes.so 0x9DC00000
+libdrm1.so 0x9D800000
+libdrm1_jni.so 0x9D400000
+libwapcore.so 0x9D000000
+libstreetview.so 0x9CC00000
+libwapbrowsertest.so 0x9C800000
+libminiglobe.so 0x9C400000
+libearth.so 0x9C000000
+libembunit.so 0x9BC00000
+libneon.so 0x9B800000
+libjni_example.so 0x9B400000
+libjni_load_test.so 0x9B000000
+libjni_lib_test.so 0x9AC00000
+librunperf.so 0x9A800000
+libctest.so 0x9A700000
+libUAPI_jni.so 0x9A500000
+librpc.so 0x9A400000
+libtrace_test.so 0x9A300000
+libsrec_jni.so 0x9A200000
diff --git a/core/process_wrapper.sh b/core/process_wrapper.sh
new file mode 100755
index 000000000..9c3104ef8
--- /dev/null
+++ b/core/process_wrapper.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# When using a process wrapper, this is the top-level
+# command that is executed instead of the server
+# command. It starts a new xterm in which the user can
+# interact with the new process.
+#
+# Inside of the xterm is a gdb session, through which
+# the user can debug the new process.
+
+# Save away these variables, since we may loose them
+# when starting in the xterm.
+export PREV_LD_LIBRARY_PATH=$LD_LIBRARY_PATH
+export PREV_PATH=$PATH
+
+gnome-terminal -t "Wrapper: $1" --disable-factory -x $2/process_wrapper_gdb.sh "$@"
+
diff --git a/core/process_wrapper_gdb.cmds b/core/process_wrapper_gdb.cmds
new file mode 100644
index 000000000..f5bdd214e
--- /dev/null
+++ b/core/process_wrapper_gdb.cmds
@@ -0,0 +1 @@
+run
diff --git a/core/process_wrapper_gdb.sh b/core/process_wrapper_gdb.sh
new file mode 100755
index 000000000..38b948ad2
--- /dev/null
+++ b/core/process_wrapper_gdb.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+# This is the command running inside the xterm of our
+# debug wrapper. It needs to take care of starting
+# the server command, so it can attach to the parent
+# process. In addition, here we run the command inside
+# of a gdb session to allow for debugging.
+
+# On some systems, running xterm will cause LD_LIBRARY_PATH
+# to be cleared, so restore it and PATH to be safe.
+export PATH=$PREV_PATH
+export LD_LIBRARY_PATH=$PREV_LD_LIBRARY_PATH
+
+# Start binderproc (or whatever sub-command is being run)
+# inside of gdb, giving gdb an initial command script to
+# automatically run the process without user intervention.
+gdb -q -x $2/process_wrapper_gdb.cmds --args "$@"
diff --git a/core/product.mk b/core/product.mk
new file mode 100644
index 000000000..8f5dc7b8a
--- /dev/null
+++ b/core/product.mk
@@ -0,0 +1,156 @@
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Functions for including AndroidProducts.mk files
+#
+
+#
+# Returns the list of all AndroidProducts.mk files.
+# $(call ) isn't necessary.
+#
+define _find-android-products-files
+$(shell test -d vendor && find vendor -maxdepth 6 -name AndroidProducts.mk) \
+ $(SRC_TARGET_DIR)/product/AndroidProducts.mk
+endef
+
+#
+# Returns the sorted concatenation of all PRODUCT_MAKEFILES
+# variables set in all AndroidProducts.mk files.
+# $(call ) isn't necessary.
+#
+define get-all-product-makefiles
+$(sort \
+ $(foreach f,$(_find-android-products-files), \
+ $(eval PRODUCT_MAKEFILES :=) \
+ $(eval LOCAL_DIR := $(patsubst %/,%,$(dir $(f)))) \
+ $(eval include $(f)) \
+ $(PRODUCT_MAKEFILES) \
+ ) \
+ $(eval PRODUCT_MAKEFILES :=) \
+ $(eval LOCAL_DIR :=) \
+ )
+endef
+
+#
+# Functions for including product makefiles
+#
+
+_product_var_list := \
+ PRODUCT_NAME \
+ PRODUCT_MODEL \
+ PRODUCT_LOCALES \
+ PRODUCT_PACKAGES \
+ PRODUCT_DEVICE \
+ PRODUCT_MANUFACTURER \
+ PRODUCT_BRAND \
+ PRODUCT_PROPERTY_OVERRIDES \
+ PRODUCT_COPY_FILES \
+ PRODUCT_OTA_PUBLIC_KEYS \
+ PRODUCT_POLICY \
+ PRODUCT_PACKAGE_OVERLAYS \
+ DEVICE_PACKAGE_OVERLAYS \
+ PRODUCT_CONTRIBUTORS_FILE \
+ PRODUCT_TAGS
+
+define dump-product
+$(info ==== $(1) ====)\
+$(foreach v,$(_product_var_list),\
+$(info PRODUCTS.$(1).$(v) := $(PRODUCTS.$(1).$(v))))\
+$(info --------)
+endef
+
+define dump-products
+$(foreach p,$(PRODUCTS),$(call dump-product,$(p)))
+endef
+
+#
+# $(1): product to inherit
+#
+define inherit-product
+ $(foreach v,$(_product_var_list), \
+ $(eval $(v) := $($(v)) $(INHERIT_TAG)$(strip $(1))))
+endef
+
+#
+# $(1): product makefile list
+#
+#TODO: check to make sure that products have all the necessary vars defined
+define import-products
+$(call import-nodes,PRODUCTS,$(1),$(_product_var_list))
+endef
+
+
+#
+# Does various consistency checks on all of the known products.
+# Takes no parameters, so $(call ) is not necessary.
+#
+define check-all-products
+$(if ,, \
+ $(eval _cap_names :=) \
+ $(foreach p,$(PRODUCTS), \
+ $(eval pn := $(strip $(PRODUCTS.$(p).PRODUCT_NAME))) \
+ $(if $(pn),,$(error $(p): PRODUCT_NAME must be defined.)) \
+ $(if $(filter $(pn),$(_cap_names)), \
+ $(error $(p): PRODUCT_NAME must be unique; "$(pn)" already used by $(strip \
+ $(foreach \
+ pp,$(PRODUCTS),
+ $(if $(filter $(pn),$(PRODUCTS.$(pp).PRODUCT_NAME)), \
+ $(pp) \
+ ))) \
+ ) \
+ ) \
+ $(eval _cap_names += $(pn)) \
+ $(if $(call is-c-identifier,$(pn)),, \
+ $(error $(p): PRODUCT_NAME must be a valid C identifier, not "$(pn)") \
+ ) \
+ $(eval pb := $(strip $(PRODUCTS.$(p).PRODUCT_BRAND))) \
+ $(if $(pb),,$(error $(p): PRODUCT_BRAND must be defined.)) \
+ $(foreach cf,$(strip $(PRODUCTS.$(p).PRODUCT_COPY_FILES)), \
+ $(if $(filter 2,$(words $(subst :,$(space),$(cf)))),, \
+ $(error $(p): malformed COPY_FILE "$(cf)") \
+ ) \
+ ) \
+ ) \
+)
+endef
+
+
+#
+# Returns the product makefile path for the product with the provided name
+#
+# $(1): short product name like "generic"
+#
+define _resolve-short-product-name
+ $(eval pn := $(strip $(1)))
+ $(eval p := \
+ $(foreach p,$(PRODUCTS), \
+ $(if $(filter $(pn),$(PRODUCTS.$(p).PRODUCT_NAME)), \
+ $(p) \
+ )) \
+ )
+ $(eval p := $(sort $(p)))
+ $(if $(filter 1,$(words $(p))), \
+ $(p), \
+ $(if $(filter 0,$(words $(p))), \
+ $(error No matches for product "$(pn)"), \
+ $(error Product "$(pn)" ambiguous: matches $(p)) \
+ ) \
+ )
+endef
+define resolve-short-product-name
+$(strip $(call _resolve-short-product-name,$(1)))
+endef
diff --git a/core/product_config.mk b/core/product_config.mk
new file mode 100644
index 000000000..93671f408
--- /dev/null
+++ b/core/product_config.mk
@@ -0,0 +1,244 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# ---------------------------------------------------------------
+# Generic functions
+# TODO: Move these to definitions.make once we're able to include
+# definitions.make before config.make.
+
+###########################################################
+## Return non-empty if $(1) is a C identifier; i.e., if it
+## matches /^[a-zA-Z_][a-zA-Z0-9_]*$/. We do this by first
+## making sure that it isn't empty and doesn't start with
+## a digit, then by removing each valid character. If the
+## final result is empty, then it was a valid C identifier.
+##
+## $(1): word to check
+###########################################################
+
+_ici_digits := 0 1 2 3 4 5 6 7 8 9
+_ici_alphaunderscore := \
+ a b c d e f g h i j k l m n o p q r s t u v w x y z \
+ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z _
+define is-c-identifier
+$(strip \
+ $(if $(1), \
+ $(if $(filter $(addsuffix %,$(_ici_digits)),$(1)), \
+ , \
+ $(eval w := $(1)) \
+ $(foreach c,$(_ici_digits) $(_ici_alphaunderscore), \
+ $(eval w := $(subst $(c),,$(w))) \
+ ) \
+ $(if $(w),,TRUE) \
+ $(eval w :=) \
+ ) \
+ ) \
+ )
+endef
+
+
+# ---------------------------------------------------------------
+# Provide "PRODUCT-<prodname>-<goal>" targets, which lets you build
+# a particular configuration without needing to set up the environment.
+#
+product_goals := $(strip $(filter PRODUCT-%,$(MAKECMDGOALS)))
+ifdef product_goals
+ # Scrape the product and build names out of the goal,
+ # which should be of the form PRODUCT-<productname>-<buildname>.
+ #
+ ifneq ($(words $(product_goals)),1)
+ $(error Only one PRODUCT-* goal may be specified; saw "$(product_goals)")
+ endif
+ goal_name := $(product_goals)
+ product_goals := $(patsubst PRODUCT-%,%,$(product_goals))
+ product_goals := $(subst -, ,$(product_goals))
+ ifneq ($(words $(product_goals)),2)
+ $(error Bad PRODUCT-* goal "$(goal_name)")
+ endif
+
+ # The product they want
+ TARGET_PRODUCT := $(word 1,$(product_goals))
+
+ # The variant they want
+ TARGET_BUILD_VARIANT := $(word 2,$(product_goals))
+
+ # HACK HACK HACK
+ # The build server wants to do make PRODUCT-dream-installclean
+ # which really means TARGET_PRODUCT=dream make installclean.
+ ifneq ($(filter-out eng user userdebug tests,$(TARGET_BUILD_VARIANT)),)
+ MAKECMDGOALS := $(MAKECMDGOALS) $(TARGET_BUILD_VARIANT)
+ TARGET_BUILD_VARIANT := eng
+ default_goal_substitution :=
+ else
+ default_goal_substitution := $(DEFAULT_GOAL)
+ endif
+ # HACK HACK HACK
+
+ # Hack to make the linux build servers use dexpreopt.
+ # OSX is still a little flaky. Most engineers don't use this
+ # type of target ("make PRODUCT-blah-user"), so this should
+ # only tend to happen when using buildbot.
+ # TODO: remove this and fix the matching lines in build/core/main.mk
+ # once dexpreopt works better on OSX.
+ ifeq ($(TARGET_BUILD_VARIANT),user)
+ WITH_DEXPREOPT_buildbot := true
+ endif
+
+ # Replace the PRODUCT-* goal with the build goal that it refers to.
+ # Note that this will ensure that it appears in the same relative
+ # position, in case it matters.
+ #
+ # Note that modifying this will not affect the goals that make will
+ # attempt to build, but it's important because we inspect this value
+ # in certain situations (like for "make sdk").
+ #
+ MAKECMDGOALS := $(patsubst $(goal_name),$(default_goal_substitution),$(MAKECMDGOALS))
+
+ # Define a rule for the PRODUCT-* goal, and make it depend on the
+ # patched-up command-line goals as well as any other goals that we
+ # want to force.
+ #
+.PHONY: $(goal_name)
+$(goal_name): $(MAKECMDGOALS)
+endif
+# else: Use the value set in the environment or buildspec.mk.
+
+# ---------------------------------------------------------------
+# Include the product definitions.
+# We need to do this to translate TARGET_PRODUCT into its
+# underlying TARGET_DEVICE before we start defining any rules.
+#
+include $(BUILD_SYSTEM)/node_fns.mk
+include $(BUILD_SYSTEM)/product.mk
+include $(BUILD_SYSTEM)/device.mk
+
+# Read in all of the product definitions specified by the AndroidProducts.mk
+# files in the tree.
+#
+#TODO: when we start allowing direct pointers to product files,
+# guarantee that they're in this list.
+$(call import-products, $(get-all-product-makefiles))
+$(check-all-products)
+#$(dump-products)
+#$(error done)
+
+# Convert a short name like "sooner" into the path to the product
+# file defining that product.
+#
+INTERNAL_PRODUCT := $(call resolve-short-product-name, $(TARGET_PRODUCT))
+#$(error TARGET_PRODUCT $(TARGET_PRODUCT) --> $(INTERNAL_PRODUCT))
+
+# Find the device that this product maps to.
+TARGET_DEVICE := $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEVICE)
+
+# Figure out which resoure configuration options to use for this
+# product.
+PRODUCT_LOCALES := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_LOCALES))
+# TODO: also keep track of things like "port", "land" in product files.
+
+# If CUSTOM_LOCALES contains any locales not already included
+# in PRODUCT_LOCALES, add them to PRODUCT_LOCALES.
+extra_locales := $(filter-out $(PRODUCT_LOCALES),$(CUSTOM_LOCALES))
+ifneq (,$(extra_locales))
+ $(info Adding CUSTOM_LOCALES [$(extra_locales)] to PRODUCT_LOCALES [$(PRODUCT_LOCALES)])
+ PRODUCT_LOCALES += $(extra_locales)
+ extra_locales :=
+endif
+
+# Assemble the list of options.
+PRODUCT_AAPT_CONFIG := $(PRODUCT_LOCALES)
+
+# Convert spaces to commas.
+comma := ,
+PRODUCT_AAPT_CONFIG := \
+ $(subst $(space),$(comma),$(strip $(PRODUCT_AAPT_CONFIG)))
+
+PRODUCT_BRAND := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_BRAND))
+
+PRODUCT_MODEL := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_MODEL))
+ifndef PRODUCT_MODEL
+ PRODUCT_MODEL := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_NAME))
+endif
+
+PRODUCT_MANUFACTURER := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_MANUFACTURER))
+ifndef PRODUCT_MANUFACTURER
+ PRODUCT_MANUFACTURER := unknown
+endif
+
+# Which policy should this product use
+PRODUCT_POLICY := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_POLICY))
+
+# A list of words like <source path>:<destination path>. The file at
+# the source path should be copied to the destination path when building
+# this product. <destination path> is relative to $(PRODUCT_OUT), so
+# it should look like, e.g., "system/etc/file.xml". The rules
+# for these copy steps are defined in config/Makefile.
+PRODUCT_COPY_FILES := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_COPY_FILES))
+
+# The HTML file containing the contributors to the project.
+PRODUCT_CONTRIBUTORS_FILE := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_CONTRIBUTORS_FILE))
+
+# A list of property assignments, like "key = value", with zero or more
+# whitespace characters on either side of the '='.
+PRODUCT_PROPERTY_OVERRIDES := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PROPERTY_OVERRIDES))
+
+# Should we use the default resources or add any product specific overlays
+PRODUCT_PACKAGE_OVERLAYS := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGE_OVERLAYS))
+DEVICE_PACKAGE_OVERLAYS := \
+ $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).DEVICE_PACKAGE_OVERLAYS))
+
+# An list of whitespace-separated words.
+PRODUCT_TAGS := $(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_TAGS))
+
+# Add the product-defined properties to the build properties.
+ADDITIONAL_BUILD_PROPERTIES := \
+ $(ADDITIONAL_BUILD_PROPERTIES) \
+ $(PRODUCT_PROPERTY_OVERRIDES)
+
+# Get the list of OTA public keys for the product.
+OTA_PUBLIC_KEYS := \
+ $(sort \
+ $(OTA_PUBLIC_KEYS) \
+ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OTA_PUBLIC_KEYS) \
+ )
+
+# HACK: Not all products define OTA keys yet, and the -user build
+# will fail if no keys are defined.
+# TODO: Let a product opt out of needing OTA keys, and stop defaulting to
+# the test key as soon as possible.
+ifeq (,$(strip $(OTA_PUBLIC_KEYS)))
+ ifeq (,$(CALLED_FROM_SETUP))
+ $(warning WARNING: adding test OTA key)
+ endif
+ OTA_PUBLIC_KEYS := $(SRC_TARGET_DIR)/product/security/testkey.x509.pem
+endif
+
+# ---------------------------------------------------------------
+# Force the simulator to be the simulator, and make BUILD_TYPE
+# default to debug.
+ifeq ($(TARGET_PRODUCT),sim)
+ TARGET_SIMULATOR := true
+ ifeq (,$(strip $(TARGET_BUILD_TYPE)))
+ TARGET_BUILD_TYPE := debug
+ endif
+ # dexpreopt doesn't work when building the simulator
+ DISABLE_DEXPREOPT := true
+endif
diff --git a/core/raw_executable.mk b/core/raw_executable.mk
new file mode 100644
index 000000000..30e0adea0
--- /dev/null
+++ b/core/raw_executable.mk
@@ -0,0 +1,26 @@
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE_SUFFIX :=
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+
+include $(BUILD_SYSTEM)/binary.mk
+
+$(LOCAL_BUILT_MODULE) : PRIVATE_ELF_FILE := $(intermediates)/$(PRIVATE_MODULE).elf
+$(LOCAL_BUILT_MODULE) : PRIVATE_LIBS := `$(TARGET_CC) -mthumb-interwork -print-libgcc-file-name`
+
+$(all_objects) : TARGET_PROJECT_INCLUDES :=
+$(all_objects) : TARGET_C_INCLUDES :=
+$(all_objects) : TARGET_GLOBAL_CFLAGS :=
+$(all_objects) : TARGET_GLOBAL_CPPFLAGS :=
+
+$(LOCAL_BUILT_MODULE): $(all_objects) $(all_libraries)
+ @$(mkdir -p $(dir $@)
+ @echo "target Linking: $(PRIVATE_MODULE)"
+ $(hide) $(TARGET_LD) \
+ $(addprefix --script ,$(PRIVATE_LINK_SCRIPT)) \
+ $(PRIVATE_RAW_EXECUTABLE_LDFLAGS) \
+ -o $(PRIVATE_ELF_FILE) \
+ $(PRIVATE_ALL_OBJECTS) \
+ --start-group $(PRIVATE_ALL_STATIC_LIBRARIES) --end-group \
+ $(PRIVATE_LIBS)
+ $(hide) $(TARGET_OBJCOPY) -O binary $(PRIVATE_ELF_FILE) $@
+
diff --git a/core/raw_static_library.mk b/core/raw_static_library.mk
new file mode 100644
index 000000000..f7b11ef70
--- /dev/null
+++ b/core/raw_static_library.mk
@@ -0,0 +1,5 @@
+
+LOCAL_RAW_STATIC_LIBRARY:=true
+
+include $(BUILD_STATIC_LIBRARY)
+
diff --git a/core/root.mk b/core/root.mk
new file mode 100644
index 000000000..6c8f7952e
--- /dev/null
+++ b/core/root.mk
@@ -0,0 +1,3 @@
+### DO NOT EDIT THIS FILE ###
+include build/core/main.mk
+### DO NOT EDIT THIS FILE ###
diff --git a/core/shared_library.mk b/core/shared_library.mk
new file mode 100644
index 000000000..a30d86849
--- /dev/null
+++ b/core/shared_library.mk
@@ -0,0 +1,32 @@
+###########################################################
+## Standard rules for building a normal shared library.
+##
+## Additional inputs from base_rules.make:
+## None.
+##
+## LOCAL_MODULE_SUFFIX will be set for you.
+###########################################################
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := SHARED_LIBRARIES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := $(TARGET_SHLIB_SUFFIX)
+endif
+ifeq ($(strip $(LOCAL_PRELINK_MODULE)),)
+LOCAL_PRELINK_MODULE := $(strip $(TARGET_PRELINK_MODULE))
+endif
+ifneq ($(strip $(OVERRIDE_BUILT_MODULE_PATH)),)
+$(error $(LOCAL_PATH): Illegal use of OVERRIDE_BUILT_MODULE_PATH)
+endif
+
+# Put the built targets of all shared libraries in a common directory
+# to simplify the link line.
+OVERRIDE_BUILT_MODULE_PATH := $(TARGET_OUT_INTERMEDIATE_LIBRARIES)
+
+include $(BUILD_SYSTEM)/dynamic_binary.mk
+
+$(linked_module): $(all_objects) $(all_libraries) \
+ $(LOCAL_ADDITIONAL_DEPENDENCIES) \
+ $(TARGET_CRTBEGIN_SO_O) $(TARGET_CRTEND_SO_O)
+ $(transform-o-to-shared-lib)
diff --git a/core/static_java_library.mk b/core/static_java_library.mk
new file mode 100644
index 000000000..93d770add
--- /dev/null
+++ b/core/static_java_library.mk
@@ -0,0 +1,25 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Standard rules for building a "static" java library.
+# Static java libraries are not installed, nor listed on any
+# classpaths. They can, however, be included wholesale in
+# other java modules.
+
+LOCAL_UNINSTALLABLE_MODULE := true
+LOCAL_IS_STATIC_JAVA_LIBRARY := true
+include $(BUILD_SYSTEM)/java_library.mk
+LOCAL_IS_STATIC_JAVA_LIBRARY :=
diff --git a/core/static_library.mk b/core/static_library.mk
new file mode 100644
index 000000000..252dfd092
--- /dev/null
+++ b/core/static_library.mk
@@ -0,0 +1,29 @@
+###########################################################
+## Standard rules for building a static library.
+##
+## Additional inputs from base_rules.make:
+## None.
+##
+## LOCAL_MODULE_SUFFIX will be set for you.
+###########################################################
+
+ifeq ($(strip $(LOCAL_MODULE_CLASS)),)
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+endif
+ifeq ($(strip $(LOCAL_MODULE_SUFFIX)),)
+LOCAL_MODULE_SUFFIX := .a
+endif
+LOCAL_UNINSTALLABLE_MODULE := true
+
+include $(BUILD_SYSTEM)/binary.mk
+
+ifeq ($(LOCAL_RAW_STATIC_LIBRARY),true)
+LOCAL_RAW_STATIC_LIBRARY:=
+$(all_objects) : TARGET_PROJECT_INCLUDES :=
+$(all_objects) : TARGET_C_INCLUDES :=
+$(all_objects) : TARGET_GLOBAL_CFLAGS :=
+$(all_objects) : TARGET_GLOBAL_CPPFLAGS :=
+endif
+
+$(LOCAL_BUILT_MODULE): $(all_objects)
+ $(transform-o-to-static-lib)
diff --git a/core/tasks/apicheck.mk b/core/tasks/apicheck.mk
new file mode 100644
index 000000000..044e4af7f
--- /dev/null
+++ b/core/tasks/apicheck.mk
@@ -0,0 +1,76 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rules for running apicheck to confirm that you haven't broken
+# api compatibility or added apis illegally.
+#
+
+ifneq ($(BUILD_TINY_ANDROID), true)
+
+.PHONY: checkapi
+
+# eval this to define a rule that runs apicheck.
+#
+# Args:
+# $(1) target
+# $(2) stable api xml file
+# $(3) api xml file to be tested
+# $(4) arguments for apicheck
+# $(5) command to run if apicheck failed
+define check-api
+$(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp: $(2) $(3) $(APICHECK)
+ @echo "Checking API:" $(1)
+ $(hide) ( $(APICHECK) $(4) $(2) $(3) || ( $(5) ; exit 38 ) )
+ $(hide) mkdir -p $$(dir $$@)
+ $(hide) touch $$@
+checkapi: $(TARGET_OUT_COMMON_INTERMEDIATES)/PACKAGING/$(strip $(1))-timestamp
+endef
+
+# Run the checkapi rules by default.
+droidcore: checkapi
+
+# INTERNAL_PLATFORM_API_FILE is the one build by droiddoc.
+
+# Check that the API we're building hasn't broken the last-released
+# SDK version.
+$(eval $(call check-api, \
+ checkapi-last, \
+ $(SRC_API_DIR)/$(lastword $(TARGET_AVAILABLE_SDK_VERSIONS)).xml, \
+ $(INTERNAL_PLATFORM_API_FILE), \
+ -hide 2 -hide 3 -hide 4 -hide 5 -hide 6 -hide 24 -hide 25 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 , \
+ cat $(BUILD_SYSTEM)/apicheck_msg_last.txt \
+ ))
+
+# Check that the API we're building hasn't changed from the not-yet-released
+# SDK version.
+$(eval $(call check-api, \
+ checkapi-current, \
+ $(SRC_API_DIR)/current.xml, \
+ $(INTERNAL_PLATFORM_API_FILE), \
+ -error 2 -error 3 -error 4 -error 5 -error 6 \
+ -error 7 -error 8 -error 9 -error 10 -error 11 -error 12 -error 13 -error 14 -error 15 \
+ -error 16 -error 17 -error 18 -error 19 -error 20 -error 21 -error 23 -error 24 \
+ -error 25 , \
+ cat $(BUILD_SYSTEM)/apicheck_msg_current.txt \
+ ))
+
+.PHONY: update-api
+update-api: $(INTERNAL_PLATFORM_API_FILE) | $(ACP)
+ @echo Copying current.xml
+ $(hide) $(ACP) $(INTERNAL_PLATFORM_API_FILE) $(SRC_API_DIR)/current.xml
+
+endif
diff --git a/core/tasks/cts.mk b/core/tasks/cts.mk
new file mode 100644
index 000000000..9ef99dbc0
--- /dev/null
+++ b/core/tasks/cts.mk
@@ -0,0 +1,95 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cts_dir := $(HOST_OUT)/cts
+cts_tools_src_dir := cts/tools
+
+cts_name := android-cts
+
+CTS_EXECUTABLE := cts
+ifeq ($(HOST_OS),windows)
+ CTS_EXECUTABLE_PATH := $(cts_tools_src_dir)/host/etc/cts.bat
+else
+ CTS_EXECUTABLE_PATH := $(HOST_OUT_EXECUTABLES)/$(CTS_EXECUTABLE)
+endif
+CTS_HOST_JAR := $(HOST_OUT_JAVA_LIBRARIES)/cts.jar
+
+CTS_CASE_LIST := \
+ DeviceInfoCollector \
+ CtsTestStubs \
+ CtsAppTestCases \
+ CtsContentTestCases \
+ CtsDatabaseTestCases \
+ CtsGraphicsTestCases \
+ CtsLocationTestCases \
+ CtsOsTestCases \
+ CtsProviderTestCases \
+ CtsTextTestCases \
+ CtsUtilTestCases \
+ CtsViewTestCases \
+ CtsWidgetTestCases \
+ CtsNetTestCases \
+ SignatureTest
+
+DEFAULT_TEST_PLAN := $(PRIVATE_DIR)/resource/plans
+
+$(cts_dir)/all_cts_files_stamp: $(CTS_CASE_LIST) | $(ACP)
+# Make necessary directory for CTS
+ @rm -rf $(PRIVATE_CTS_DIR)
+ @mkdir -p $(TMP_DIR)
+ @mkdir -p $(PRIVATE_DIR)/docs
+ @mkdir -p $(PRIVATE_DIR)/tools
+ @mkdir -p $(PRIVATE_DIR)/repository/testcases
+ @mkdir -p $(PRIVATE_DIR)/repository/plans
+# Copy executable to CTS directory
+ $(hide) $(ACP) -fp $(CTS_HOST_JAR) $(PRIVATE_DIR)/tools
+ $(hide) $(ACP) -fp $(CTS_EXECUTABLE_PATH) $(PRIVATE_DIR)/tools
+# Change mode of the executables
+ $(hide) chmod ug+rwX $(PRIVATE_DIR)/tools/$(notdir $(CTS_EXECUTABLE_PATH))
+ $(foreach apk,$(CTS_CASE_LIST), \
+ $(call copy-testcase-apk,$(apk)))
+# Copy CTS host config and start script to CTS directory
+ $(hide) $(ACP) -fp $(cts_tools_src_dir)/utils/host_config.xml $(PRIVATE_DIR)/repository/
+ $(hide) $(ACP) -fp $(cts_tools_src_dir)/utils/startcts $(PRIVATE_DIR)/tools/
+ $(hide) touch $@
+
+# Generate the default test plan for User.
+$(DEFAULT_TEST_PLAN): $(cts_dir)/all_cts_files_stamp $(cts_tools_src_dir)/utils/genDefaultTestPlan.sh
+ $(hide) bash $(cts_tools_src_dir)/utils/genDefaultTestPlan.sh cts/tests/tests/ \
+ $(PRIVATE_DIR) $(TMP_DIR) $(TOP) $(TARGET_COMMON_OUT_ROOT) $(OUT_DIR)
+
+# Package CTS and clean up.
+#
+# TODO:
+# Pack cts.bat into the same zip file as well. See http://buganizer/issue?id=1656821 for more details
+INTERNAL_CTS_TARGET := $(cts_dir)/$(cts_name).zip
+$(INTERNAL_CTS_TARGET): PRIVATE_NAME := $(cts_name)
+$(INTERNAL_CTS_TARGET): PRIVATE_CTS_DIR := $(cts_dir)
+$(INTERNAL_CTS_TARGET): PRIVATE_DIR := $(cts_dir)/$(cts_name)
+$(INTERNAL_CTS_TARGET): TMP_DIR := $(cts_dir)/temp
+$(INTERNAL_CTS_TARGET): $(cts_dir)/all_cts_files_stamp $(DEFAULT_TEST_PLAN)
+ @echo "Package CTS: $@"
+ $(hide) cd $(dir $@) && zip -rq $(notdir $@) $(PRIVATE_NAME)
+
+.PHONY: cts
+cts: $(INTERNAL_CTS_TARGET) adb
+$(call dist-for-goals,cts,$(INTERNAL_CTS_TARGET))
+
+define copy-testcase-apk
+
+$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(1))/package.apk \
+ $(PRIVATE_DIR)/repository/testcases/$(1).apk
+
+endef
+
diff --git a/core/tasks/localize.mk b/core/tasks/localize.mk
new file mode 100644
index 000000000..12e7b5cb9
--- /dev/null
+++ b/core/tasks/localize.mk
@@ -0,0 +1,47 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Rules for building the xlb files for export for translation.
+#
+
+# Gather all of the resource files for the default locale -- that is,
+# all resources in directories called values or values-something, where
+# one of the - separated segments is not two characters long -- those are the
+# language directories, and we don't want those.
+all_resource_files := $(foreach pkg, \
+ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_PACKAGES), \
+ $(PACKAGES.$(pkg).RESOURCE_FILES))
+values_resource_files := $(shell echo $(all_resource_files) | \
+ tr -s / | \
+ tr " " "\n" | \
+ grep -E "\/values[^/]*/(strings.xml|arrays.xml)$$" | \
+ grep -v -E -e "-[a-zA-Z]{2}[/\-]")
+
+xlb_target := $(PRODUCT_OUT)/strings.xlb
+
+$(xlb_target): $(values_resource_files) | $(LOCALIZE)
+ @echo XLB: $@
+ $(hide) mkdir -p $(dir $@)
+ $(hide) rm -f $@
+ $(hide) $(LOCALIZE) xlb $@ $^
+
+# Add a phony target so typing make xlb is convenient
+.PHONY: xlb
+xlb: $(xlb_target)
+
+# We want this on the build-server builds, but no reason to inflict it on
+# everyone
+$(call dist-for-goals, droid, $(xlb_target))
+
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
new file mode 100644
index 000000000..e38a80366
--- /dev/null
+++ b/core/version_defaults.mk
@@ -0,0 +1,84 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Handle various build version information.
+#
+# Guarantees that the following are defined:
+# PLATFORM_VERSION
+# PLATFORM_SDK_VERSION
+# BUILD_ID
+# BUILD_NUMBER
+#
+
+# Look for an optional file containing overrides of the defaults,
+# but don't cry if we don't find it. We could just use -include, but
+# the build.prop target also wants INTERNAL_BUILD_ID_MAKEFILE to be set
+# if the file exists.
+#
+INTERNAL_BUILD_ID_MAKEFILE := $(wildcard $(BUILD_SYSTEM)/build_id.mk)
+ifneq "" "$(INTERNAL_BUILD_ID_MAKEFILE)"
+ include $(INTERNAL_BUILD_ID_MAKEFILE)
+endif
+
+ifeq "" "$(PLATFORM_VERSION)"
+ # This is the canonical definition of the platform version,
+ # which is the version that we reveal to the end user.
+ # Update this value when the platform version changes (rather
+ # than overriding it somewhere else). Can be an arbitrary string.
+ PLATFORM_VERSION := 1.5
+endif
+
+ifeq "" "$(PLATFORM_SDK_VERSION)"
+ # This is the canonical definition of the SDK version, which defines
+ # the set of APIs and functionality available in the platform. This is
+ # a single integer, that increases monotonically as updates to the SDK
+ # are released.
+ PLATFORM_SDK_VERSION := 3
+endif
+
+ifeq "" "$(BUILD_ID)"
+ # Used to signify special builds. E.g., branches and/or releases,
+ # like "M5-RC7". Can be an arbitrary string, but must be a single
+ # word and a valid file name.
+ #
+ # If there is no BUILD_ID set, make it obvious.
+ BUILD_ID := UNKNOWN
+endif
+
+ifeq "" "$(BUILD_NUMBER)"
+ # BUILD_NUMBER should be set to the source control value that
+ # represents the current state of the source code. E.g., a
+ # perforce changelist number or a git hash. Can be an arbitrary string
+ # (to allow for source control that uses something other than numbers),
+ # but must be a single word and a valid file name.
+ #
+ # If no BUILD_NUMBER is set, create a useful "I am an engineering build
+ # from this date/time" value. Make it start with a non-digit so that
+ # anyone trying to parse it as an integer will probably get "0".
+ BUILD_NUMBER := eng.$(USER).$(shell date +%Y%m%d.%H%M%S)
+endif
+
+ifeq "true" "$(DISPLAY_BUILD_NUMBER)"
+ # if the build_id.mk has this defined, then BUILD_ID is updated with
+ # the BUILD_NUMBER as well. For development branches, this will be
+ # set, but release branches this will not be set.
+ BUILD_DISPLAY_ID := "$(BUILD_ID).$(BUILD_NUMBER)"
+else
+ BUILD_DISPLAY_ID := "$(BUILD_ID)"
+endif
+
+
diff --git a/envsetup.sh b/envsetup.sh
new file mode 100644
index 000000000..016c399ba
--- /dev/null
+++ b/envsetup.sh
@@ -0,0 +1,1037 @@
+function help() {
+cat <<EOF
+Invoke ". build/envsetup.sh" from your shell to add the following functions to your environment:
+- croot: Changes directory to the top of the tree.
+- m: Makes from the top of the tree.
+- mm: Builds all of the modules in the current directory.
+- mmm: Builds all of the modules in the supplied directories.
+- cgrep: Greps on all local C/C++ files.
+- jgrep: Greps on all local Java files.
+- resgrep: Greps on all local res/*.xml files.
+- godir: Go to the directory containing a file.
+
+Look at the source to view more functions. The complete list is:
+EOF
+ T=$(gettop)
+ local A
+ A=""
+ for i in `cat $T/build/envsetup.sh | sed -n "/^function /s/function \([a-z_]*\).*/\1/p" | sort`; do
+ A="$A $i"
+ done
+ echo $A
+}
+
+# Get the value of a build variable as an absolute path.
+function get_abs_build_var()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+ CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
+ make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-abs-$1
+}
+
+# Get the exact value of a build variable.
+function get_build_var()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+ CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
+ make --no-print-directory -C "$T" -f build/core/config.mk dumpvar-$1
+}
+
+# check to see if the supplied product is one we can build
+function check_product()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+ CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \
+ TARGET_PRODUCT=$1 TARGET_BUILD_VARIANT= \
+ TARGET_SIMULATOR= TARGET_BUILD_TYPE= \
+ get_build_var TARGET_DEVICE > /dev/null
+ # hide successful answers, but allow the errors to show
+}
+
+VARIANT_CHOICES=(user userdebug eng)
+
+# check to see if the supplied variant is valid
+function check_variant()
+{
+ for v in ${VARIANT_CHOICES[@]}
+ do
+ if [ "$v" = "$1" ]
+ then
+ return 0
+ fi
+ done
+ return 1
+}
+
+function setpaths()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ return
+ fi
+
+ ##################################################################
+ # #
+ # Read me before you modify this code #
+ # #
+ # This function sets ANDROID_BUILD_PATHS to what it is adding #
+ # to PATH, and the next time it is run, it removes that from #
+ # PATH. This is required so lunch can be run more than once #
+ # and still have working paths. #
+ # #
+ ##################################################################
+
+ # out with the old
+ if [ -n $ANDROID_BUILD_PATHS ] ; then
+ export PATH=${PATH/$ANDROID_BUILD_PATHS/}
+ fi
+
+ # and in with the new
+ CODE_REVIEWS=
+ prebuiltdir=$(getprebuilt)
+ export ANDROID_EABI_TOOLCHAIN=$prebuiltdir/toolchain/arm-eabi-4.2.1/bin
+ export ANDROID_TOOLCHAIN=$ANDROID_EABI_TOOLCHAIN
+ export ANDROID_QTOOLS=$T/development/emulator/qtools
+ export ANDROID_BUILD_PATHS=:$(get_build_var ANDROID_BUILD_PATHS):$ANDROID_QTOOLS:$ANDROID_TOOLCHAIN:$ANDROID_EABI_TOOLCHAIN$CODE_REVIEWS
+ export PATH=$PATH$ANDROID_BUILD_PATHS
+
+ unset ANDROID_PRODUCT_OUT
+ export ANDROID_PRODUCT_OUT=$(get_abs_build_var PRODUCT_OUT)
+ export OUT=$ANDROID_PRODUCT_OUT
+
+ # needed for building linux on MacOS
+ # TODO: fix the path
+ #export HOST_EXTRACFLAGS="-I "$T/system/kernel_headers/host_include
+
+ # needed for OProfile to post-process collected samples
+ export OPROFILE_EVENTS_DIR=$prebuiltdir/oprofile
+}
+
+function printconfig()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+ get_build_var report_config
+}
+
+function set_stuff_for_environment()
+{
+ settitle
+ setpaths
+ set_sequence_number
+
+ # Don't try to do preoptimization until it works better on OSX.
+ export DISABLE_DEXPREOPT=true
+
+ export ANDROID_BUILD_TOP=$(gettop)
+}
+
+function set_sequence_number()
+{
+ export BUILD_ENV_SEQUENCE_NUMBER=9
+}
+
+function settitle()
+{
+ if [ "$STAY_OFF_MY_LAWN" = "" ]; then
+ local product=$(get_build_var TARGET_PRODUCT)
+ local variant=$(get_build_var TARGET_BUILD_VARIANT)
+ export PROMPT_COMMAND="echo -ne \"\033]0;[${product}-${variant}] ${USER}@${HOSTNAME}: ${PWD}\007\""
+ fi
+}
+
+case `uname -s` in
+ Linux)
+ function choosesim()
+ {
+ echo "Build for the simulator or the device?"
+ echo " 1. Device"
+ echo " 2. Simulator"
+ echo
+
+ export TARGET_SIMULATOR=
+ local ANSWER
+ while [ -z $TARGET_SIMULATOR ]
+ do
+ echo -n "Which would you like? [1] "
+ if [ -z "$1" ] ; then
+ read ANSWER
+ else
+ echo $1
+ ANSWER=$1
+ fi
+ case $ANSWER in
+ "")
+ export TARGET_SIMULATOR=false
+ ;;
+ 1)
+ export TARGET_SIMULATOR=false
+ ;;
+ Device)
+ export TARGET_SIMULATOR=false
+ ;;
+ 2)
+ export TARGET_SIMULATOR=true
+ ;;
+ Simulator)
+ export TARGET_SIMULATOR=true
+ ;;
+ *)
+ echo
+ echo "I didn't understand your response. Please try again."
+ echo
+ ;;
+ esac
+ if [ -n "$1" ] ; then
+ break
+ fi
+ done
+
+ set_stuff_for_environment
+ }
+ ;;
+ *)
+ function choosesim()
+ {
+ echo "Only device builds are supported for" `uname -s`
+ echo " Forcing TARGET_SIMULATOR=false"
+ echo
+ if [ -z "$1" ]
+ then
+ echo -n "Press enter: "
+ read
+ fi
+
+ export TARGET_SIMULATOR=false
+ set_stuff_for_environment
+ }
+ ;;
+esac
+
+function choosetype()
+{
+ echo "Build type choices are:"
+ echo " 1. release"
+ echo " 2. debug"
+ echo
+
+ local DEFAULT_NUM DEFAULT_VALUE
+ if [ $TARGET_SIMULATOR = "false" ] ; then
+ DEFAULT_NUM=1
+ DEFAULT_VALUE=release
+ else
+ DEFAULT_NUM=2
+ DEFAULT_VALUE=debug
+ fi
+
+ export TARGET_BUILD_TYPE=
+ local ANSWER
+ while [ -z $TARGET_BUILD_TYPE ]
+ do
+ echo -n "Which would you like? ["$DEFAULT_NUM"] "
+ if [ -z "$1" ] ; then
+ read ANSWER
+ else
+ echo $1
+ ANSWER=$1
+ fi
+ case $ANSWER in
+ "")
+ export TARGET_BUILD_TYPE=$DEFAULT_VALUE
+ ;;
+ 1)
+ export TARGET_BUILD_TYPE=release
+ ;;
+ release)
+ export TARGET_BUILD_TYPE=release
+ ;;
+ 2)
+ export TARGET_BUILD_TYPE=debug
+ ;;
+ debug)
+ export TARGET_BUILD_TYPE=debug
+ ;;
+ *)
+ echo
+ echo "I didn't understand your response. Please try again."
+ echo
+ ;;
+ esac
+ if [ -n "$1" ] ; then
+ break
+ fi
+ done
+
+ set_stuff_for_environment
+}
+
+#
+# This function isn't really right: It chooses a TARGET_PRODUCT
+# based on the list of boards. Usually, that gets you something
+# that kinda works with a generic product, but really, you should
+# pick a product by name.
+#
+function chooseproduct()
+{
+ # Find the makefiles that must exist for a product.
+ # Send stderr to /dev/null in case partner isn't present.
+ local -a choices
+ choices=(`/bin/ls build/target/board/*/BoardConfig.mk vendor/*/*/BoardConfig.mk 2> /dev/null`)
+
+ local choice
+ local -a prodlist
+ for choice in ${choices[@]}
+ do
+ # The product name is the name of the directory containing
+ # the makefile we found, above.
+ prodlist=(${prodlist[@]} `dirname ${choice} | xargs basename`)
+ done
+
+ local index=1
+ local p
+ echo "Product choices are:"
+ for p in ${prodlist[@]}
+ do
+ echo " $index. $p"
+ let "index = $index + 1"
+ done
+
+
+ if [ "x$TARGET_PRODUCT" != x ] ; then
+ default_value=$TARGET_PRODUCT
+ else
+ if [ "$TARGET_SIMULATOR" = true ] ; then
+ default_value=sim
+ else
+ default_value=generic
+ fi
+ fi
+
+ export TARGET_PRODUCT=
+ local ANSWER
+ while [ -z "$TARGET_PRODUCT" ]
+ do
+ echo "You can also type the name of a product if you know it."
+ echo -n "Which would you like? [$default_value] "
+ if [ -z "$1" ] ; then
+ read ANSWER
+ else
+ echo $1
+ ANSWER=$1
+ fi
+
+ if [ -z "$ANSWER" ] ; then
+ export TARGET_PRODUCT=$default_value
+ elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then
+ local poo=`echo -n $ANSWER`
+ if [ $poo -le ${#prodlist[@]} ] ; then
+ export TARGET_PRODUCT=${prodlist[$(($ANSWER-$_arrayoffset))]}
+ else
+ echo "** Bad product selection: $ANSWER"
+ fi
+ else
+ if check_product $ANSWER
+ then
+ export TARGET_PRODUCT=$ANSWER
+ else
+ echo "** Not a valid product: $ANSWER"
+ fi
+ fi
+ if [ -n "$1" ] ; then
+ break
+ fi
+ done
+
+ set_stuff_for_environment
+}
+
+function choosevariant()
+{
+ echo "Variant choices are:"
+ local index=1
+ local v
+ for v in ${VARIANT_CHOICES[@]}
+ do
+ # The product name is the name of the directory containing
+ # the makefile we found, above.
+ echo " $index. $v"
+ index=$(($index+1))
+ done
+
+ local default_value=eng
+ local ANSWER
+
+ export TARGET_BUILD_VARIANT=
+ while [ -z "$TARGET_BUILD_VARIANT" ]
+ do
+ echo -n "Which would you like? [$default_value] "
+ if [ -z "$1" ] ; then
+ read ANSWER
+ else
+ echo $1
+ ANSWER=$1
+ fi
+
+ if [ -z "$ANSWER" ] ; then
+ export TARGET_BUILD_VARIANT=$default_value
+ elif (echo -n $ANSWER | grep -q -e "^[0-9][0-9]*$") ; then
+ if [ "$ANSWER" -le "${#VARIANT_CHOICES[@]}" ] ; then
+ export TARGET_BUILD_VARIANT=${VARIANT_CHOICES[$(($ANSWER-$_arrayoffset))]}
+ fi
+ else
+ if check_variant $ANSWER
+ then
+ export TARGET_BUILD_VARIANT=$ANSWER
+ else
+ echo "** Not a valid variant: $ANSWER"
+ fi
+ fi
+ if [ -n "$1" ] ; then
+ break
+ fi
+ done
+}
+
+function tapas()
+{
+ choosecombo
+}
+
+function choosecombo()
+{
+ choosesim $1
+
+ echo
+ echo
+ choosetype $2
+
+ echo
+ echo
+ chooseproduct $3
+
+ echo
+ echo
+ choosevariant $4
+
+ echo
+ set_stuff_for_environment
+ printconfig
+}
+
+# Clear this variable. It will be built up again when the vendorsetup.sh
+# files are included at the end of this file.
+unset LUNCH_MENU_CHOICES
+function add_lunch_combo()
+{
+ local new_combo=$1
+ local c
+ for c in ${LUNCH_MENU_CHOICES[@]} ; do
+ if [ "$new_combo" = "$c" ] ; then
+ return
+ fi
+ done
+ LUNCH_MENU_CHOICES=(${LUNCH_MENU_CHOICES[@]} $new_combo)
+}
+
+# add the default one here
+add_lunch_combo generic-eng
+
+# if we're on linux, add the simulator. There is a special case
+# in lunch to deal with the simulator
+if [ "$(uname)" = "Linux" ] ; then
+ add_lunch_combo simulator
+fi
+
+function print_lunch_menu()
+{
+ local uname=$(uname)
+ echo
+ echo "You're building on" $uname
+ echo
+ echo ${LUNCH_MENU_CHOICES[@]}
+ echo "Lunch menu... pick a combo:"
+
+ local i=1
+ local choice
+ for choice in ${LUNCH_MENU_CHOICES[@]}
+ do
+ echo " $i. $choice"
+ i=$(($i+1))
+ done
+
+ echo
+}
+
+function lunch()
+{
+ local answer
+
+ if [ "$1" ] ; then
+ answer=$1
+ else
+ print_lunch_menu
+ echo -n "Which would you like? [generic-eng] "
+ read answer
+ fi
+
+ local selection=
+
+ if [ -z "$answer" ]
+ then
+ selection=generic-eng
+ elif [ "$answer" = "simulator" ]
+ then
+ selection=simulator
+ elif (echo -n $answer | grep -q -e "^[0-9][0-9]*$")
+ then
+ if [ $answer -le ${#LUNCH_MENU_CHOICES[@]} ]
+ then
+ selection=${LUNCH_MENU_CHOICES[$(($answer-$_arrayoffset))]}
+ fi
+ elif (echo -n $answer | grep -q -e "^[^\-][^\-]*-[^\-][^\-]*$")
+ then
+ selection=$answer
+ fi
+
+ if [ -z "$selection" ]
+ then
+ echo
+ echo "Invalid lunch combo: $answer"
+ return 1
+ fi
+
+ # special case the simulator
+ if [ "$selection" = "simulator" ]
+ then
+ export TARGET_PRODUCT=sim
+ export TARGET_BUILD_VARIANT=eng
+ export TARGET_SIMULATOR=true
+ export TARGET_BUILD_TYPE=debug
+ else
+ local product=$(echo -n $selection | sed -e "s/-.*$//")
+ check_product $product
+ if [ $? -ne 0 ]
+ then
+ echo
+ echo "** Don't have a product spec for: '$product'"
+ echo "** Do you have the right repo manifest?"
+ product=
+ fi
+
+ local variant=$(echo -n $selection | sed -e "s/^[^\-]*-//")
+ check_variant $variant
+ if [ $? -ne 0 ]
+ then
+ echo
+ echo "** Invalid variant: '$variant'"
+ echo "** Must be one of ${VARIANT_CHOICES[@]}"
+ variant=
+ fi
+
+ if [ -z "$product" -o -z "$variant" ]
+ then
+ echo
+ return 1
+ fi
+
+ export TARGET_PRODUCT=$product
+ export TARGET_BUILD_VARIANT=$variant
+ export TARGET_SIMULATOR=false
+ export TARGET_BUILD_TYPE=release
+ fi # !simulator
+
+ echo
+
+ set_stuff_for_environment
+ printconfig
+}
+
+function gettop
+{
+ local TOPFILE=build/core/envsetup.mk
+ if [ -n "$TOP" -a -f "$TOP/$TOPFILE" ] ; then
+ echo $TOP
+ else
+ if [ -f $TOPFILE ] ; then
+ echo $PWD
+ else
+ # We redirect cd to /dev/null in case it's aliased to
+ # a command that prints something as a side-effect
+ # (like pushd)
+ local HERE=$PWD
+ T=
+ while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
+ cd .. > /dev/null
+ T=$PWD
+ done
+ cd $HERE > /dev/null
+ if [ -f "$T/$TOPFILE" ]; then
+ echo $T
+ fi
+ fi
+ fi
+}
+
+function m()
+{
+ T=$(gettop)
+ if [ "$T" ]; then
+ make -C $T $@
+ else
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ fi
+}
+
+function findmakefile()
+{
+ TOPFILE=build/core/envsetup.mk
+ # We redirect cd to /dev/null in case it's aliased to
+ # a command that prints something as a side-effect
+ # (like pushd)
+ local HERE=$PWD
+ T=
+ while [ \( ! \( -f $TOPFILE \) \) -a \( $PWD != "/" \) ]; do
+ T=$PWD
+ if [ -f "$T/Android.mk" ]; then
+ echo $T/Android.mk
+ cd $HERE > /dev/null
+ return
+ fi
+ cd .. > /dev/null
+ done
+ cd $HERE > /dev/null
+}
+
+function mm()
+{
+ # If we're sitting in the root of the build tree, just do a
+ # normal make.
+ if [ -f build/core/envsetup.mk -a -f Makefile ]; then
+ make $@
+ else
+ # Find the closest Android.mk file.
+ T=$(gettop)
+ local M=$(findmakefile)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ elif [ ! "$M" ]; then
+ echo "Couldn't locate a makefile from the current directory."
+ else
+ ONE_SHOT_MAKEFILE=$M make -C $T files $@
+ fi
+ fi
+}
+
+function mmm()
+{
+ T=$(gettop)
+ if [ "$T" ]; then
+ local MAKEFILE=
+ local ARGS=
+ local DIR TO_CHOP
+ local DASH_ARGS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^-.*$/')
+ local DIRS=$(echo "$@" | awk -v RS=" " -v ORS=" " '/^[^-].*$/')
+ for DIR in $DIRS ; do
+ DIR=`echo $DIR | sed -e 's:/$::'`
+ if [ -f $DIR/Android.mk ]; then
+ TO_CHOP=`echo $T | wc -c | tr -d ' '`
+ TO_CHOP=`expr $TO_CHOP + 1`
+ MFILE=`echo $PWD | cut -c${TO_CHOP}-`
+ if [ "$MFILE" = "" ] ; then
+ MFILE=$DIR/Android.mk
+ else
+ MFILE=$MFILE/$DIR/Android.mk
+ fi
+ MAKEFILE="$MAKEFILE $MFILE"
+ else
+ if [ "$DIR" = snod ]; then
+ ARGS="$ARGS snod"
+ elif [ "$DIR" = showcommands ]; then
+ ARGS="$ARGS showcommands"
+ else
+ echo "No Android.mk in $DIR."
+ fi
+ fi
+ done
+ ONE_SHOT_MAKEFILE="$MAKEFILE" make -C $T $DASH_ARGS files $ARGS
+ else
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ fi
+}
+
+function croot()
+{
+ T=$(gettop)
+ if [ "$T" ]; then
+ cd $(gettop)
+ else
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ fi
+}
+
+function pid()
+{
+ local EXE="$1"
+ if [ "$EXE" ] ; then
+ local PID=`adb shell ps | fgrep $1 | sed -e 's/[^ ]* *\([0-9]*\).*/\1/'`
+ echo "$PID"
+ else
+ echo "usage: pid name"
+ fi
+}
+
+function gdbclient()
+{
+ local OUT_ROOT=$(get_abs_build_var PRODUCT_OUT)
+ local OUT_SYMBOLS=$(get_abs_build_var TARGET_OUT_UNSTRIPPED)
+ local OUT_SO_SYMBOLS=$(get_abs_build_var TARGET_OUT_SHARED_LIBRARIES_UNSTRIPPED)
+ local OUT_EXE_SYMBOLS=$(get_abs_build_var TARGET_OUT_EXECUTABLES_UNSTRIPPED)
+ local PREBUILTS=$(get_abs_build_var ANDROID_PREBUILTS)
+ if [ "$OUT_ROOT" -a "$PREBUILTS" ]; then
+ local EXE="$1"
+ if [ "$EXE" ] ; then
+ EXE=$1
+ else
+ EXE="app_process"
+ fi
+
+ local PORT="$2"
+ if [ "$PORT" ] ; then
+ PORT=$2
+ else
+ PORT=":5039"
+ fi
+
+ local PID
+ local PROG="$3"
+ if [ "$PROG" ] ; then
+ PID=`pid $3`
+ adb forward "tcp$PORT" "tcp$PORT"
+ adb shell gdbserver $PORT --attach $PID &
+ sleep 2
+ else
+ echo ""
+ echo "If you haven't done so already, do this first on the device:"
+ echo " gdbserver $PORT /system/bin/$EXE"
+ echo " or"
+ echo " gdbserver $PORT --attach $PID"
+ echo ""
+ fi
+
+ echo >|"$OUT_ROOT/gdbclient.cmds" "set solib-absolute-prefix $OUT_SYMBOLS"
+ echo >>"$OUT_ROOT/gdbclient.cmds" "set solib-search-path $OUT_SO_SYMBOLS"
+ echo >>"$OUT_ROOT/gdbclient.cmds" "target remote $PORT"
+ echo >>"$OUT_ROOT/gdbclient.cmds" ""
+
+ arm-eabi-gdb -x "$OUT_ROOT/gdbclient.cmds" "$OUT_EXE_SYMBOLS/$EXE"
+ else
+ echo "Unable to determine build system output dir."
+ fi
+
+}
+
+case `uname -s` in
+ Darwin)
+ function sgrep()
+ {
+ find -E . -type f -iregex '.*\.(c|h|cpp|S|java|xml)' -print0 | xargs -0 grep --color -n "$@"
+ }
+
+ ;;
+ *)
+ function sgrep()
+ {
+ find . -type f -iregex '.*\.\(c\|h\|cpp\|S\|java\|xml\)' -print0 | xargs -0 grep --color -n "$@"
+ }
+ ;;
+esac
+
+function jgrep()
+{
+ find . -type f -name "*\.java" -print0 | xargs -0 grep --color -n "$@"
+}
+
+function cgrep()
+{
+ find . -type f -name "*\.c*" -print0 | xargs -0 grep --color -n "$@"
+}
+
+function resgrep()
+{
+ for dir in `find . -name res -type d`; do find $dir -type f -name '*\.xml' -print0 | xargs -0 grep --color -n "$@"; done;
+}
+
+case `uname -s` in
+ Darwin)
+ function mgrep()
+ {
+ find -E . -type f -iregex '.*/(Makefile|Makefile\..*|.*\.make|.*\.mak|.*\.mk)' -print0 | xargs -0 grep --color -n "$@"
+ }
+
+ function treegrep()
+ {
+ find -E . -type f -iregex '.*\.(c|h|cpp|S|java|xml)' -print0 | xargs -0 grep --color -n -i "$@"
+ }
+
+ ;;
+ *)
+ function mgrep()
+ {
+ find . -regextype posix-egrep -iregex '(.*\/Makefile|.*\/Makefile\..*|.*\.make|.*\.mak|.*\.mk)' -type f -print0 | xargs -0 grep --color -n "$@"
+ }
+
+ function treegrep()
+ {
+ find . -regextype posix-egrep -iregex '.*\.(c|h|cpp|S|java|xml)' -type f -print0 | xargs -0 grep --color -n -i "$@"
+ }
+
+ ;;
+esac
+
+function getprebuilt
+{
+ get_abs_build_var ANDROID_PREBUILTS
+}
+
+function tracedmdump()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP."
+ return
+ fi
+ local prebuiltdir=$(getprebuilt)
+ local KERNEL=$T/prebuilt/android-arm/vmlinux-qemu
+
+ local TRACE=$1
+ if [ ! "$TRACE" ] ; then
+ echo "usage: tracedmdump tracename"
+ return
+ fi
+
+ local BASETRACE=$(basename $TRACE)
+ if [ "$BASETRACE" = "$TRACE" ] ; then
+ TRACE=$ANDROID_PRODUCT_OUT/traces/$TRACE
+ fi
+
+ echo "post-processing traces..."
+ rm -f $TRACE/qtrace.dexlist
+ post_trace $TRACE
+ if [ $? -ne 0 ]; then
+ echo "***"
+ echo "*** Error: malformed trace. Did you remember to exit the emulator?"
+ echo "***"
+ return
+ fi
+ echo "generating dexlist output..."
+ /bin/ls $ANDROID_PRODUCT_OUT/system/framework/*.jar $ANDROID_PRODUCT_OUT/system/app/*.apk $ANDROID_PRODUCT_OUT/data/app/*.apk 2>/dev/null | xargs dexlist > $TRACE/qtrace.dexlist
+ echo "generating dmtrace data..."
+ q2dm -r $ANDROID_PRODUCT_OUT/symbols $TRACE $KERNEL $TRACE/dmtrace || return
+ echo "generating html file..."
+ dmtracedump -h $TRACE/dmtrace >| $TRACE/dmtrace.html || return
+ echo "done, see $TRACE/dmtrace.html for details"
+ echo "or run:"
+ echo " traceview $TRACE/dmtrace"
+}
+
+# communicate with a running device or emulator, set up necessary state,
+# and run the hat command.
+function runhat()
+{
+ # process standard adb options
+ local adbTarget=""
+ if [ $1 = "-d" -o $1 = "-e" ]; then
+ adbTarget=$1
+ shift 1
+ elif [ $1 = "-s" ]; then
+ adbTarget="$1 $2"
+ shift 2
+ fi
+ local adbOptions=${adbTarget}
+ echo adbOptions = ${adbOptions}
+
+ # runhat options
+ local targetPid=$1
+ local outputFile=$2
+
+ if [ "$targetPid" = "" ]; then
+ echo "Usage: runhat [ -d | -e | -s serial ] target-pid [output-file]"
+ return
+ fi
+
+ # confirm hat is available
+ if [ -z $(which hat) ]; then
+ echo "hat is not available in this configuration."
+ return
+ fi
+
+ adb ${adbOptions} shell >/dev/null mkdir /data/misc
+ adb ${adbOptions} shell chmod 777 /data/misc
+
+ # send a SIGUSR1 to cause the hprof dump
+ echo "Poking $targetPid and waiting for data..."
+ adb ${adbOptions} shell kill -10 $targetPid
+ echo "Press enter when logcat shows \"hprof: heap dump completed\""
+ echo -n "> "
+ read
+
+ local availFiles=( $(adb ${adbOptions} shell ls /data/misc | grep '^heap-dump' | sed -e 's/.*heap-dump-/heap-dump-/' | sort -r | tr '[:space:][:cntrl:]' ' ') )
+ local devFile=/data/misc/${availFiles[0]}
+ local localFile=/tmp/$$-hprof
+
+ echo "Retrieving file $devFile..."
+ adb ${adbOptions} pull $devFile $localFile
+
+ adb ${adbOptions} shell rm $devFile
+
+ echo "Running hat on $localFile"
+ echo "View the output by pointing your browser at http://localhost:7000/"
+ echo ""
+ hat $localFile
+}
+
+function getbugreports()
+{
+ local reports=(`adb shell ls /sdcard/bugreports | tr -d '\r'`)
+
+ if [ ! "$reports" ]; then
+ echo "Could not locate any bugreports."
+ return
+ fi
+
+ local report
+ for report in ${reports[@]}
+ do
+ echo "/sdcard/bugreports/${report}"
+ adb pull /sdcard/bugreports/${report} ${report}
+ gunzip ${report}
+ done
+}
+
+function startviewserver()
+{
+ local port=4939
+ if [ $# -gt 0 ]; then
+ port=$1
+ fi
+ adb shell service call window 1 i32 $port
+}
+
+function stopviewserver()
+{
+ adb shell service call window 2
+}
+
+function isviewserverstarted()
+{
+ adb shell service call window 3
+}
+
+function smoketest()
+{
+ if [ ! "$ANDROID_PRODUCT_OUT" ]; then
+ echo "Couldn't locate output files. Try running 'lunch' first." >&2
+ return
+ fi
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+
+ (cd "$T" && mmm tests/SmokeTest) &&
+ adb uninstall com.android.smoketest > /dev/null &&
+ adb uninstall com.android.smoketest.tests > /dev/null &&
+ adb install $ANDROID_PRODUCT_OUT/data/app/SmokeTestApp.apk &&
+ adb install $ANDROID_PRODUCT_OUT/data/app/SmokeTest.apk &&
+ adb shell am instrument -w com.android.smoketest.tests/android.test.InstrumentationTestRunner
+}
+
+# simple shortcut to the runtest command
+function runtest()
+{
+ T=$(gettop)
+ if [ ! "$T" ]; then
+ echo "Couldn't locate the top of the tree. Try setting TOP." >&2
+ return
+ fi
+ (cd "$T" && development/tools/runtest $@)
+}
+
+function godir () {
+ if [[ -z "$1" ]]; then
+ echo "Usage: godir <regex>"
+ return
+ fi
+ if [[ ! -f $T/filelist ]]; then
+ echo -n "Creating index..."
+ (cd $T; find . -wholename ./out -prune -o -type f > filelist)
+ echo " Done"
+ echo ""
+ fi
+ local lines
+ lines=($(grep "$1" $T/filelist | sed -e 's/\/[^/]*$//' | sort | uniq))
+ if [[ ${#lines[@]} = 0 ]]; then
+ echo "Not found"
+ return
+ fi
+ local pathname
+ local choice
+ if [[ ${#lines[@]} > 1 ]]; then
+ while [[ -z "$pathname" ]]; do
+ local index=1
+ local line
+ for line in ${lines[@]}; do
+ printf "%6s %s\n" "[$index]" $line
+ index=$(($index + 1))
+ done
+ echo
+ echo -n "Select one: "
+ unset choice
+ read choice
+ if [[ $choice -gt ${#lines[@]} || $choice -lt 1 ]]; then
+ echo "Invalid choice"
+ continue
+ fi
+ pathname=${lines[$(($choice-$_arrayoffset))]}
+ done
+ else
+ # even though zsh arrays are 1-based, $foo[0] is an alias for $foo[1]
+ pathname=${lines[0]}
+ fi
+ cd $T/$pathname
+}
+
+# determine whether arrays are zero-based (bash) or one-based (zsh)
+_xarray=(a b c)
+if [ -z "${_xarray[${#_xarray[@]}]}" ]
+then
+ _arrayoffset=1
+else
+ _arrayoffset=0
+fi
+unset _xarray
+
+# Execute the contents of any vendorsetup.sh files we can find.
+for f in `/bin/ls vendor/*/vendorsetup.sh 2> /dev/null`
+do
+ echo "including $f"
+ . $f
+done
+unset f
diff --git a/libs/host/Android.mk b/libs/host/Android.mk
new file mode 100644
index 000000000..81f2cc5c4
--- /dev/null
+++ b/libs/host/Android.mk
@@ -0,0 +1,26 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ CopyFile.c \
+ Directories.cpp \
+ pseudolocalize.cpp
+
+ifeq ($(HOST_OS),cygwin)
+LOCAL_CFLAGS += -DWIN32_EXE
+endif
+ifeq ($(HOST_OS),darwin)
+LOCAL_CFLAGS += -DMACOSX_RSRC
+endif
+ifeq ($(HOST_OS),linux)
+endif
+
+LOCAL_MODULE:= libhost
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+
+# acp uses libhost, so we can't use
+# acp to install libhost.
+LOCAL_ACP_UNAVAILABLE:= true
+
+include $(BUILD_HOST_STATIC_LIBRARY)
+
diff --git a/libs/host/CopyFile.c b/libs/host/CopyFile.c
new file mode 100644
index 000000000..a822b412f
--- /dev/null
+++ b/libs/host/CopyFile.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Android "cp" replacement.
+ *
+ * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead
+ * of utime(), and getxattr()/setxattr() instead of chmod(). These are
+ * probably "better", but are non-portable, and not necessary for our
+ * purposes.
+ */
+#include <host/CopyFile.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+
+#ifdef HAVE_MS_C_RUNTIME
+# define mkdir(path,mode) _mkdir(path)
+#endif
+
+#ifndef HAVE_SYMLINKS
+# define lstat stat
+# ifndef EACCESS /* seems to be missing from the Mingw headers */
+# define EACCESS 13
+# endif
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
+
+/*#define DEBUG_MSGS*/
+#ifdef DEBUG_MSGS
+# define DBUG(x) printf x
+#else
+# define DBUG(x) ((void)0)
+#endif
+
+#define FSSEP '/' /* filename separator char */
+
+static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options);
+
+/*
+ * Returns true if the source file is newer than the destination file.
+ *
+ * The check is based on the modification date, whole seconds only. This
+ * also returns true if the file sizes don't match.
+ */
+static bool isSourceNewer(const struct stat* pSrcStat, const struct stat* pDstStat)
+{
+ return (pSrcStat->st_mtime > pDstStat->st_mtime) ||
+ (pSrcStat->st_size != pDstStat->st_size);
+}
+
+/*
+ * Returns true if the source and destination files are actually the
+ * same thing. We detect this by checking the inode numbers, which seems
+ * to work on Cygwin.
+ */
+static bool isSameFile(const struct stat* pSrcStat, const struct stat* pDstStat)
+{
+#ifndef HAVE_VALID_STAT_ST_INO
+ /* with MSVCRT.DLL, stat always sets st_ino to 0, and there is no simple way to */
+ /* get the equivalent information with Win32 (Cygwin does some weird stuff in */
+ /* its winsup/cygwin/fhandler_disk_file.cc to emulate this, too complex for us) */
+ return 0;
+#else
+ return (pSrcStat->st_ino == pDstStat->st_ino);
+#endif
+}
+
+static void printCopyMsg(const char* src, const char* dst, unsigned int options)
+{
+ if ((options & COPY_VERBOSE_MASK) > 0)
+ printf(" '%s' --> '%s'\n", src, dst);
+}
+
+static void printNotNewerMsg(const char* src, const char* dst, unsigned int options)
+{
+ if ((options & COPY_VERBOSE_MASK) > 1)
+ printf(" '%s' is up-to-date\n", dst);
+}
+
+/*
+ * Copy the contents of one file to another.
+ *
+ * The files are assumed to be seeked to the start.
+ */
+static int copyFileContents(const char* dst, int dstFd, const char* src, int srcFd)
+{
+ unsigned char buf[8192];
+ ssize_t readCount, writeCount;
+
+ /*
+ * Read a chunk, write it, and repeat.
+ */
+ while (1) {
+ readCount = read(srcFd, buf, sizeof(buf));
+ if (readCount < 0) {
+ fprintf(stderr,
+ "acp: failed reading '%s': %s\n", src, strerror(errno));
+ return -1;
+ }
+
+ if (readCount > 0) {
+ writeCount = write(dstFd, buf, readCount);
+ if (writeCount < 0) {
+ fprintf(stderr,
+ "acp: failed writing '%s': %s\n", dst, strerror(errno));
+ return -1;
+ }
+ if (writeCount != readCount) {
+ fprintf(stderr, "acp: partial write to '%s' (%d of %d)\n",
+ dst, writeCount, readCount);
+ return -1;
+ }
+ }
+
+ if (readCount < (ssize_t) sizeof(buf))
+ break;
+ }
+
+ return 0;
+}
+
+/*
+ * Set the permissions, owner, and timestamps on the destination file
+ * equal to those of the source file.
+ *
+ * Failures here are "soft"; they don't produce warning messages and don't
+ * cause the cp command to report a failure.
+ */
+static int setPermissions(const char* dst, const struct stat* pSrcStat, unsigned int options)
+{
+ struct utimbuf ut;
+
+ if (options & COPY_TIMESTAMPS) {
+ /*
+ * Start with timestamps. The access and mod dates are not affected
+ * by the next operations.
+ */
+ ut.actime = pSrcStat->st_atime;
+ ut.modtime = pSrcStat->st_mtime;
+ if (utime(dst, &ut) != 0) {
+ DBUG(("--- unable to set timestamps on '%s': %s\n",
+ dst, strerror(errno)));
+ }
+ }
+
+ if (options & COPY_PERMISSIONS) {
+ /*
+ * Set the permissions.
+ */
+ if (chmod(dst, pSrcStat->st_mode & ~(S_IFMT)) != 0) {
+ DBUG(("--- unable to set perms on '%s' to 0%o: %s\n",
+ dst, pSrcStat->st_mode & ~(S_IFMT), strerror(errno)));
+ }
+#ifndef HAVE_MS_C_RUNTIME
+ /*
+ * Set the owner.
+ */
+ if (chown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0) {
+ DBUG(("--- unable to set owner of '%s' to %d/%d: %s\n",
+ dst, pSrcStat->st_uid, pSrcStat->st_gid, strerror(errno)));
+ }
+#endif
+ }
+
+ return 0;
+}
+
+/*
+ * Copy a regular file. If the destination file exists and is not a
+ * regular file, we fail. However, we use stat() rather than lstat(),
+ * because it's okay to write through a symlink (the noDereference stuff
+ * only applies to the source file).
+ *
+ * If the file doesn't exist, create it. If it does exist, truncate it.
+ */
+static int copyRegular(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
+{
+ struct stat dstStat;
+ int srcFd, dstFd, statResult, copyResult;
+
+ DBUG(("--- copying regular '%s' to '%s'\n", src, dst));
+
+ statResult = stat(dst, &dstStat);
+ if (statResult == 0 && !S_ISREG(dstStat.st_mode)) {
+ fprintf(stderr,
+ "acp: destination '%s' exists and is not regular file\n",
+ dst);
+ return -1;
+ } else if (statResult != 0 && errno != ENOENT) {
+ fprintf(stderr, "acp: unable to stat destination '%s'\n", dst);
+ return -1;
+ }
+
+ if (statResult == 0) {
+ if (isSameFile(pSrcStat, &dstStat)) {
+ fprintf(stderr, "acp: '%s' and '%s' are the same file\n",
+ src, dst);
+ return -1;
+ }
+ if (options & COPY_UPDATE_ONLY) {
+ if (!isSourceNewer(pSrcStat, &dstStat)) {
+ DBUG(("--- source is not newer: '%s'\n", src));
+ printNotNewerMsg(src, dst, options);
+ return 0;
+ }
+ }
+ }
+
+ /* open src */
+ srcFd = open(src, O_RDONLY | O_BINARY, 0);
+ if (srcFd < 0) {
+ fprintf(stderr, "acp: unable to open '%s': %s\n", src, strerror(errno));
+ return -1;
+ }
+
+ /* open dest with O_CREAT | O_TRUNC */
+ DBUG(("--- opening '%s'\n", dst));
+ dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
+
+ if (dstFd < 0) {
+ if (errno == ENOENT) {
+ /* this happens if the target directory doesn't exist */
+ fprintf(stderr,
+ "acp: cannot create '%s': %s\n", dst, strerror(errno));
+ (void) close(srcFd);
+ return -1;
+ }
+
+ /* if "force" is set, try removing the destination file and retry */
+ if (options & COPY_FORCE) {
+ if (unlink(dst) != 0) {
+#ifdef HAVE_MS_C_RUNTIME
+ /* MSVCRT.DLL unlink will fail with EACCESS if the file is set read-only */
+ /* so try to change its mode, and unlink again */
+ if (errno == EACCESS) {
+ if (chmod(dst, S_IWRITE|S_IREAD) == 0 && unlink(dst) == 0)
+ goto Open_File;
+ }
+#endif
+ fprintf(stderr, "acp: unable to remove '%s': %s\n",
+ dst, strerror(errno));
+ (void) close(srcFd);
+ return -1;
+ }
+#ifdef HAVE_MS_C_RUNTIME
+ Open_File:
+#endif
+ dstFd = open(dst, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY, 0644);
+ }
+ }
+ if (dstFd < 0) {
+ fprintf(stderr, "acp: unable to open '%s': %s\n",
+ dst, strerror(errno));
+ (void) close(srcFd);
+ return -1;
+ }
+
+ copyResult = copyFileContents(dst, dstFd, src, srcFd);
+
+ (void) close(srcFd);
+ (void) close(dstFd);
+ if (copyResult != 0)
+ return -1;
+
+#ifdef MACOSX_RSRC
+ {
+ char* srcRsrcName = NULL;
+ char* dstRsrcName = NULL;
+ struct stat rsrcStat;
+
+ srcRsrcName = malloc(strlen(src) + 5 + 1);
+ strcpy(srcRsrcName, src);
+ strcat(srcRsrcName, "/rsrc");
+
+ dstRsrcName = malloc(strlen(dst) + 5 + 1);
+ strcpy(dstRsrcName, dst);
+ strcat(dstRsrcName, "/rsrc");
+
+ if (stat(srcRsrcName, &rsrcStat) == 0 && rsrcStat.st_size > 0) {
+ DBUG(("--- RSRC: %s --> %s\n", srcRsrcName, dstRsrcName));
+
+ srcFd = open(srcRsrcName, O_RDONLY);
+ dstFd = open(dstRsrcName, O_TRUNC | O_WRONLY, 0);
+ copyResult = -1;
+ if (srcFd >= 0 && dstFd >= 0) {
+ copyResult = copyFileContents(dstRsrcName, dstFd,
+ srcRsrcName, srcFd);
+ (void) close(srcFd);
+ (void) close(dstFd);
+ }
+
+ if (copyResult != 0)
+ return -1;
+ }
+
+ free(srcRsrcName);
+ free(dstRsrcName);
+ }
+#endif
+
+ setPermissions(dst, pSrcStat, options);
+
+ printCopyMsg(src, dst, options);
+
+ return 0;
+}
+
+
+#ifdef HAVE_SYMLINKS
+/*
+ * Copy a symlink. This only happens if we're in "no derefence" mode,
+ * in which we copy the links rather than the files that are pointed at.
+ *
+ * We always discard the destination file. If it's a symlink already,
+ * we want to throw it out and replace it. If it's not a symlink, we
+ * need to trash it so we can create one.
+ */
+static int copySymlink(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
+{
+ struct stat dstStat;
+ char linkBuf[PATH_MAX+1];
+ int statResult, nameLen;
+
+ assert(options & COPY_NO_DEREFERENCE);
+ DBUG(("--- copying symlink '%s' to '%s'\n", src, dst));
+
+ /* NOTE: we use lstat() here */
+ statResult = lstat(dst, &dstStat);
+ if (statResult == 0 && !S_ISREG(dstStat.st_mode)
+ && !S_ISLNK(dstStat.st_mode)
+ )
+ {
+ fprintf(stderr,
+ "acp: destination '%s' exists and is not regular or symlink\n",
+ dst);
+ return -1;
+ }
+
+ if (statResult == 0) {
+ if (isSameFile(pSrcStat, &dstStat)) {
+ fprintf(stderr, "acp: '%s' and '%s' are the same file\n",
+ src, dst);
+ return -1;
+ }
+ if (options & COPY_UPDATE_ONLY) {
+ if (!isSourceNewer(pSrcStat, &dstStat)) {
+ DBUG(("--- source is not newer: '%s'\n", src));
+ printNotNewerMsg(src, dst, options);
+ return 0;
+ }
+ }
+ }
+
+ /* extract the symlink contents */
+ nameLen = readlink(src, linkBuf, sizeof(linkBuf)-1);
+ if (nameLen <= 0) {
+ fprintf(stderr, "acp: unable to read symlink '%s': %s\n",
+ src, strerror(errno));
+ return -1;
+ }
+ linkBuf[nameLen] = '\0';
+ DBUG(("--- creating symlink file '%s' (--> %s)\n", dst, linkBuf));
+
+ if (statResult == 0) {
+ DBUG(("--- removing '%s'\n", dst));
+ if (unlink(dst) != 0) {
+ fprintf(stderr, "acp: unable to remove '%s': %s\n",
+ dst, strerror(errno));
+ return -1;
+ }
+ }
+
+ if (symlink(linkBuf, dst) != 0) {
+ fprintf(stderr, "acp: unable to create symlink '%s' [%s]: %s\n",
+ dst, linkBuf, strerror(errno));
+ return -1;
+ }
+
+ /*
+ * There's no way to set the file date or access permissions, but
+ * it is possible to set the owner.
+ */
+ if (options & COPY_PERMISSIONS) {
+ if (lchown(dst, pSrcStat->st_uid, pSrcStat->st_gid) != 0)
+ DBUG(("--- lchown failed: %s\n", strerror(errno)));
+ }
+
+ printCopyMsg(src, dst, options);
+
+ return 0;
+}
+#endif /* HAVE_SYMLINKS */
+
+/*
+ * Copy the contents of one directory to another. Both "src" and "dst"
+ * must be directories. We will create "dst" if it does not exist.
+ */
+int copyDirectory(const char* src, const char* dst, const struct stat* pSrcStat, unsigned int options)
+{
+ int retVal = 0;
+ struct stat dstStat;
+ DIR* dir;
+ int cc, statResult;
+
+ DBUG(("--- copy dir '%s' to '%s'\n", src, dst));
+
+ statResult = stat(dst, &dstStat);
+ if (statResult == 0 && !S_ISDIR(dstStat.st_mode)) {
+ fprintf(stderr,
+ "acp: destination '%s' exists and is not a directory\n", dst);
+ return -1;
+ } else if (statResult != 0 && errno != ENOENT) {
+ fprintf(stderr, "acp: unable to stat destination '%s'\n", dst);
+ return -1;
+ }
+
+ if (statResult == 0) {
+ if (isSameFile(pSrcStat, &dstStat)) {
+ fprintf(stderr,
+ "acp: cannot copy directory into itself ('%s' and '%s')\n",
+ src, dst);
+ return -1;
+ }
+ } else {
+ DBUG(("--- creating dir '%s'\n", dst));
+ cc = mkdir(dst, 0755);
+ if (cc != 0) {
+ fprintf(stderr, "acp: unable to create directory '%s': %s\n",
+ dst, strerror(errno));
+ return -1;
+ }
+
+ /* only print on mkdir */
+ printCopyMsg(src, dst, options);
+ }
+
+ /*
+ * Open the directory, and plow through its contents.
+ */
+ dir = opendir(src);
+ if (dir == NULL) {
+ fprintf(stderr, "acp: unable to open directory '%s': %s\n",
+ src, strerror(errno));
+ return -1;
+ }
+
+ while (1) {
+ struct dirent* ent;
+ char* srcFile;
+ char* dstFile;
+ int srcLen, dstLen, nameLen;
+
+ ent = readdir(dir);
+ if (ent == NULL)
+ break;
+
+ if (strcmp(ent->d_name, ".") == 0 ||
+ strcmp(ent->d_name, "..") == 0)
+ {
+ continue;
+ }
+
+ nameLen = strlen(ent->d_name);
+ srcLen = strlen(src);
+ dstLen = strlen(dst);
+
+ srcFile = malloc(srcLen +1 + nameLen +1);
+ memcpy(srcFile, src, srcLen);
+ srcFile[srcLen] = FSSEP;
+ memcpy(srcFile + srcLen+1, ent->d_name, nameLen +1);
+
+ dstFile = malloc(dstLen +1 + nameLen +1);
+ memcpy(dstFile, dst, dstLen);
+ dstFile[dstLen] = FSSEP;
+ memcpy(dstFile + dstLen+1, ent->d_name, nameLen +1);
+
+ if (copyFileRecursive(srcFile, dstFile, false, options) != 0)
+ retVal = -1; /* note failure and keep going */
+
+ free(srcFile);
+ free(dstFile);
+ }
+ closedir(dir);
+
+ setPermissions(dst, pSrcStat, options);
+
+ return retVal;
+}
+
+/*
+ * Do the actual copy. This is called recursively from copyDirectory().
+ *
+ * "dst" should only be a directory if "src" is also a directory.
+ *
+ * Returns 0 on success.
+ */
+static int copyFileRecursive(const char* src, const char* dst, bool isCmdLine, unsigned int options)
+{
+ char* srcExe = NULL;
+ char* dstExe = NULL;
+ char* dstDir = NULL;
+ struct stat srcStat;
+ int retVal = 0;
+ int statResult, statErrno;
+
+ /*
+ * Stat the source file. If it doesn't exist, fail.
+ */
+ if (options & COPY_NO_DEREFERENCE)
+ statResult = lstat(src, &srcStat);
+ else
+ statResult = stat(src, &srcStat);
+ statErrno = errno; /* preserve across .exe attempt */
+
+#ifdef WIN32_EXE
+ /*
+ * Here's the interesting part. Under Cygwin, if you have a file
+ * called "foo.exe", stat("foo", ...) will succeed, but open("foo", ...)
+ * will fail. We need to figure out what its name is supposed to be
+ * so we can create the correct destination file.
+ *
+ * If we don't have the "-e" flag set, we want "acp foo bar" to fail,
+ * not automatically find "foo.exe". That way, if we really were
+ * trying to copy "foo", it doesn't grab something we don't want.
+ */
+ if (isCmdLine && statResult == 0) {
+ int tmpFd;
+ tmpFd = open(src, O_RDONLY | O_BINARY, 0);
+ if (tmpFd < 0) {
+ statResult = -1;
+ statErrno = ENOENT;
+ } else {
+ (void) close(tmpFd);
+ }
+ }
+
+ /*
+ * If we didn't find the file, try it again with ".exe".
+ */
+ if (isCmdLine && statResult < 0 && statErrno == ENOENT && (options & COPY_TRY_EXE)) {
+ srcExe = malloc(strlen(src) + 4 +1);
+ strcpy(srcExe, src);
+ strcat(srcExe, ".exe");
+
+ if (options & COPY_NO_DEREFERENCE)
+ statResult = lstat(srcExe, &srcStat);
+ else
+ statResult = stat(srcExe, &srcStat);
+
+ if (statResult == 0 && !S_ISREG(srcStat.st_mode))
+ statResult = -1; /* fail, use original statErrno below */
+
+ if (statResult == 0) {
+ /* found a .exe, copy that instead */
+ dstExe = malloc(strlen(dst) + 4 +1);
+ strcpy(dstExe, dst);
+ strcat(dstExe, ".exe");
+
+ src = srcExe;
+ dst = dstExe;
+ } else {
+ DBUG(("--- couldn't find '%s' either\n", srcExe));
+ }
+ }
+#endif
+ if (statResult < 0) {
+ if (statErrno == ENOENT)
+ fprintf(stderr, "acp: file '%s' does not exist\n", src);
+ else
+ fprintf(stderr, "acp: unable to stat '%s': %s\n",
+ src, strerror(statErrno));
+ retVal = -1;
+ goto bail;
+ }
+
+ /*
+ * If "src" is a directory, ignore it if "recursive" isn't set.
+ *
+ * We want to create "dst" as a directory (or verify that it already
+ * exists as a directory), and then copy its contents.
+ */
+ if (S_ISDIR(srcStat.st_mode)) {
+ if (!(options & COPY_RECURSIVE)) {
+ fprintf(stderr, "acp: omitting directory '%s'\n", src);
+ } else {
+ retVal = copyDirectory(src, dst, &srcStat, options);
+ }
+#ifdef HAVE_SYMLINKS
+ } else if (S_ISLNK(srcStat.st_mode)) {
+ retVal = copySymlink(src, dst, &srcStat, options);
+#endif
+ } else if (S_ISREG(srcStat.st_mode)) {
+ retVal = copyRegular(src, dst, &srcStat, options);
+ } else {
+ fprintf(stderr, "acp: skipping unusual file '%s' (mode=0%o)\n",
+ src, srcStat.st_mode);
+ retVal = -1;
+ }
+
+bail:
+ free(srcExe);
+ free(dstExe);
+ free(dstDir);
+ return retVal;
+}
+
+int copyFile(const char* src, const char* dst, unsigned int options)
+{
+ return copyFileRecursive(src, dst, true, options);
+}
+
+
diff --git a/libs/host/Directories.cpp b/libs/host/Directories.cpp
new file mode 100644
index 000000000..a34f5b7bc
--- /dev/null
+++ b/libs/host/Directories.cpp
@@ -0,0 +1,42 @@
+#include <host/Directories.h>
+#include <utils/String8.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef HAVE_MS_C_RUNTIME
+#include <direct.h>
+#endif
+
+using namespace android;
+using namespace std;
+
+string
+parent_dir(const string& path)
+{
+ return string(String8(path.c_str()).getPathDir().string());
+}
+
+int
+mkdirs(const char* last)
+{
+ String8 dest;
+ const char* s = last-1;
+ int err;
+ do {
+ s++;
+ if (s > last && (*s == '.' || *s == 0)) {
+ String8 part(last, s-last);
+ dest.appendPath(part);
+#ifdef HAVE_MS_C_RUNTIME
+ err = _mkdir(dest.string());
+#else
+ err = mkdir(dest.string(), S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP);
+#endif
+ if (err != 0) {
+ return err;
+ }
+ last = s+1;
+ }
+ } while (*s);
+ return 0;
+}
diff --git a/libs/host/include/host/CopyFile.h b/libs/host/include/host/CopyFile.h
new file mode 100644
index 000000000..e65712b27
--- /dev/null
+++ b/libs/host/include/host/CopyFile.h
@@ -0,0 +1,30 @@
+#ifndef _HOST_COPYFILE_H
+#define _HOST_COPYFILE_H
+
+#include <stdbool.h>
+#include <sys/stat.h>
+
+#if __cplusplus
+extern "C" {
+#endif
+
+// command line options
+enum {
+ COPY_NO_DEREFERENCE = 0x00010000, // copy symlink link instead of target
+ COPY_TRY_EXE = 0x00020000, // on Win32, try adding '.exe' to filename
+ COPY_FORCE = 0x00040000, // override access permissions
+ COPY_PERMISSIONS = 0x00080000, // preserve mode, ownership, timestamps
+ COPY_TIMESTAMPS = 0x00100000, // preserve mode, ownership, timestamps
+ COPY_RECURSIVE = 0x00200000, // copy directories
+ COPY_UPDATE_ONLY = 0x00400000, // only copy if source file is newer
+ COPY_VERBOSE_MASK = 0x000000ff // talk lots
+};
+
+int copyFile(const char* src, const char* dst, unsigned int options);
+
+#if __cplusplus
+} // extern "C"
+#endif
+
+#endif // _HOST_COPYFILE_H
+
diff --git a/libs/host/include/host/Directories.h b/libs/host/include/host/Directories.h
new file mode 100644
index 000000000..fccce467d
--- /dev/null
+++ b/libs/host/include/host/Directories.h
@@ -0,0 +1,10 @@
+#ifndef HOST_MKDIRS_H
+#define HOST_MKDIRS_H
+
+#include <string>
+
+std::string parent_dir(const std::string& path);
+
+extern "C" int mkdirs(const char* path);
+
+#endif // HOST_MKDIRS_H
diff --git a/libs/host/include/host/pseudolocalize.h b/libs/host/include/host/pseudolocalize.h
new file mode 100644
index 000000000..94cb034ac
--- /dev/null
+++ b/libs/host/include/host/pseudolocalize.h
@@ -0,0 +1,9 @@
+#ifndef HOST_PSEUDOLOCALIZE_H
+#define HOST_PSEUDOLOCALIZE_H
+
+#include <string>
+
+std::string pseudolocalize_string(const std::string& source);
+
+#endif // HOST_PSEUDOLOCALIZE_H
+
diff --git a/libs/host/list.java b/libs/host/list.java
new file mode 100644
index 000000000..30546e3ae
--- /dev/null
+++ b/libs/host/list.java
@@ -0,0 +1,35 @@
+import java.io.*;
+
+public class list {
+ private static char nibble(int c) {
+ return (char)(c < 10 ? ('0' + c) : ('a' + (c-10)));
+ }
+ public static void main(String[] argv)
+ {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream(100);
+ OutputStreamWriter writer = null;
+ try {
+ writer = new OutputStreamWriter(stream, "utf-8");
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace(System.err);
+ }
+
+ int n = Integer.parseInt(argv[1], 16);
+ try {
+ writer.write(n);
+ writer.close();
+ } catch (IOException e) {
+ e.printStackTrace(System.err);
+ }
+
+ byte[] array = stream.toByteArray();
+
+ System.out.print(" case '" + argv[0] + "': return \"");
+ for (int i=0; i<array.length; i++) {
+ int b = array[i];
+ System.out.print("\\x" + nibble((b >> 4) & 0x0f) + nibble(b & 0xf));
+ }
+ System.out.println("\";");
+ }
+}
+
diff --git a/libs/host/pseudolocalize.cpp b/libs/host/pseudolocalize.cpp
new file mode 100644
index 000000000..a2b3c2f10
--- /dev/null
+++ b/libs/host/pseudolocalize.cpp
@@ -0,0 +1,119 @@
+#include <host/pseudolocalize.h>
+
+using namespace std;
+
+static const char*
+pseudolocalize_char(char c)
+{
+ switch (c) {
+ case 'a': return "\xc4\x83";
+ case 'b': return "\xcf\x84";
+ case 'c': return "\xc4\x8b";
+ case 'd': return "\xc4\x8f";
+ case 'e': return "\xc4\x99";
+ case 'f': return "\xc6\x92";
+ case 'g': return "\xc4\x9d";
+ case 'h': return "\xd1\x9b";
+ case 'i': return "\xcf\x8a";
+ case 'j': return "\xc4\xb5";
+ case 'k': return "\xc4\xb8";
+ case 'l': return "\xc4\xba";
+ case 'm': return "\xe1\xb8\xbf";
+ case 'n': return "\xd0\xb8";
+ case 'o': return "\xcf\x8c";
+ case 'p': return "\xcf\x81";
+ case 'q': return "\x51";
+ case 'r': return "\xd2\x91";
+ case 's': return "\xc5\xa1";
+ case 't': return "\xd1\x82";
+ case 'u': return "\xce\xb0";
+ case 'v': return "\x56";
+ case 'w': return "\xe1\xba\x85";
+ case 'x': return "\xd1\x85";
+ case 'y': return "\xe1\xbb\xb3";
+ case 'z': return "\xc5\xba";
+ case 'A': return "\xc3\x85";
+ case 'B': return "\xce\xb2";
+ case 'C': return "\xc4\x88";
+ case 'D': return "\xc4\x90";
+ case 'E': return "\xd0\x84";
+ case 'F': return "\xce\x93";
+ case 'G': return "\xc4\x9e";
+ case 'H': return "\xc4\xa6";
+ case 'I': return "\xd0\x87";
+ case 'J': return "\xc4\xb5";
+ case 'K': return "\xc4\xb6";
+ case 'L': return "\xc5\x81";
+ case 'M': return "\xe1\xb8\xbe";
+ case 'N': return "\xc5\x83";
+ case 'O': return "\xce\x98";
+ case 'P': return "\xcf\x81";
+ case 'Q': return "\x71";
+ case 'R': return "\xd0\xaf";
+ case 'S': return "\xc8\x98";
+ case 'T': return "\xc5\xa6";
+ case 'U': return "\xc5\xa8";
+ case 'V': return "\xce\xbd";
+ case 'W': return "\xe1\xba\x84";
+ case 'X': return "\xc3\x97";
+ case 'Y': return "\xc2\xa5";
+ case 'Z': return "\xc5\xbd";
+ default: return NULL;
+ }
+}
+
+/**
+ * Converts characters so they look like they've been localized.
+ *
+ * Note: This leaves escape sequences untouched so they can later be
+ * processed by ResTable::collectString in the normal way.
+ */
+string
+pseudolocalize_string(const string& source)
+{
+ const char* s = source.c_str();
+ string result;
+ const size_t I = source.length();
+ for (size_t i=0; i<I; i++) {
+ char c = s[i];
+ if (c == '\\') {
+ if (i<I-1) {
+ result += '\\';
+ i++;
+ c = s[i];
+ switch (c) {
+ case 'u':
+ // this one takes up 5 chars
+ result += string(s+i, 5);
+ i += 4;
+ break;
+ case 't':
+ case 'n':
+ case '#':
+ case '@':
+ case '?':
+ case '"':
+ case '\'':
+ case '\\':
+ default:
+ result += c;
+ break;
+ }
+ } else {
+ result += c;
+ }
+ } else {
+ const char* p = pseudolocalize_char(c);
+ if (p != NULL) {
+ result += p;
+ } else {
+ result += c;
+ }
+ }
+ }
+
+ //printf("result=\'%s\'\n", result.c_str());
+ return result;
+}
+
+
diff --git a/target/board/Android.mk b/target/board/Android.mk
new file mode 100644
index 000000000..64e3a7458
--- /dev/null
+++ b/target/board/Android.mk
@@ -0,0 +1,54 @@
+#
+# Set up product-global definitions and include product-specific rules.
+#
+
+ifneq ($(strip $(TARGET_NO_BOOTLOADER)),true)
+ INSTALLED_BOOTLOADER_MODULE := $(PRODUCT_OUT)/bootloader
+ ifeq ($(strip $(TARGET_BOOTLOADER_IS_2ND)),true)
+ INSTALLED_2NDBOOTLOADER_TARGET := $(PRODUCT_OUT)/2ndbootloader
+ else
+ INSTALLED_2NDBOOTLOADER_TARGET :=
+ endif
+else
+ INSTALLED_BOOTLOADER_MODULE :=
+ INSTALLED_2NDBOOTLOADER_TARGET :=
+endif # TARGET_NO_BOOTLOADER
+
+ifneq ($(strip $(TARGET_NO_KERNEL)),true)
+ INSTALLED_KERNEL_TARGET := $(PRODUCT_OUT)/kernel
+else
+ INSTALLED_KERNEL_TARGET :=
+endif
+
+ifneq ($(strip $(TARGET_NO_RADIOIMAGE)),true)
+ INSTALLED_RADIOIMAGE_TARGET := $(PRODUCT_OUT)/radio.img
+else
+ INSTALLED_RADIOIMAGE_TARGET :=
+endif
+
+ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/AndroidBoard.mk))
+ ifeq (,$(wildcard $(TARGET_DEVICE_DIR)/Android.mk))
+ $(error Missing "$(TARGET_DEVICE_DIR)/AndroidBoard.mk")
+ else
+ # TODO: Remove this check after people have had a chance to switch,
+ # after April 2009.
+ $(error Please rename "$(TARGET_DEVICE_DIR)/Android.mk" to "$(TARGET_DEVICE_DIR)/AndroidBoard.mk")
+ endif
+endif
+include $(TARGET_DEVICE_DIR)/AndroidBoard.mk
+
+# Generate a file that contains various information about the
+# device we're building for. This file is typically packaged up
+# with everything else.
+#
+# If the file "board-info.txt" appears in $(TARGET_DEVICE_DIR),
+# it will be appended to the output file.
+#
+INSTALLED_ANDROID_INFO_TXT_TARGET := $(PRODUCT_OUT)/android-info.txt
+board_info_txt := $(wildcard $(TARGET_DEVICE_DIR)/board-info.txt)
+$(INSTALLED_ANDROID_INFO_TXT_TARGET): $(board_info_txt)
+ $(call pretty,"Generated: ($@)")
+ $(hide) echo "board=$(TARGET_BOOTLOADER_BOARD_NAME)" > $@
+ifdef board_info_txt
+ $(hide) cat $< >> $@
+endif
diff --git a/target/board/emulator/AndroidBoard.mk b/target/board/emulator/AndroidBoard.mk
new file mode 100644
index 000000000..09badee23
--- /dev/null
+++ b/target/board/emulator/AndroidBoard.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+file := $(TARGET_OUT_KEYLAYOUT)/tuttle2.kl
+ALL_PREBUILT += $(file)
+$(file) : $(LOCAL_PATH)/tuttle2.kl | $(ACP)
+ $(transform-prebuilt-to-target)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := tuttle2.kcm
+include $(BUILD_KEY_CHAR_MAP)
diff --git a/target/board/emulator/BoardConfig.mk b/target/board/emulator/BoardConfig.mk
new file mode 100644
index 000000000..784118aaf
--- /dev/null
+++ b/target/board/emulator/BoardConfig.mk
@@ -0,0 +1,9 @@
+# config.mk
+#
+# Product-specific compile-time definitions.
+#
+
+# The generic product target doesn't have any hardware-specific pieces.
+TARGET_NO_BOOTLOADER := true
+TARGET_NO_KERNEL := true
+HAVE_HTC_AUDIO_DRIVER := true
diff --git a/target/board/emulator/README.txt b/target/board/emulator/README.txt
new file mode 100644
index 000000000..6a1ec89e8
--- /dev/null
+++ b/target/board/emulator/README.txt
@@ -0,0 +1,10 @@
+The "emulator" product defines an almost non-hardware-specific target
+without a kernel or bootloader, except that it defines the
+HAVE_HTC_AUDIO_DRIVER constant, since that is what the emulator
+emulates currently.
+
+It can be used to build the entire user-level system, and
+will work with the emulator.
+
+It is not a product "base class"; no other products inherit
+from it or use it in any way.
diff --git a/target/board/emulator/tuttle2.kcm b/target/board/emulator/tuttle2.kcm
new file mode 100644
index 000000000..0a2dd8c99
--- /dev/null
+++ b/target/board/emulator/tuttle2.kcm
@@ -0,0 +1,66 @@
+[type=QWERTY]
+
+# keycode display number base caps fn caps_fn
+
+A 'A' '%' 'a' 'A' '%' 0x00
+B 'B' '=' 'b' 'B' '=' 0x00
+C 'C' '8' 'c' 'C' '8' 0x00E7
+D 'D' '5' 'd' 'D' '5' 0x00
+E 'E' '2' 'e' 'E' '2' 0x0301
+F 'F' '6' 'f' 'F' '6' 0x00A5
+G 'G' '-' 'g' 'G' '-' '_'
+H 'H' '[' 'h' 'H' '[' '{'
+I 'I' '$' 'i' 'I' '$' 0x0302
+J 'J' ']' 'j' 'J' ']' '}'
+K 'K' '"' 'k' 'K' '"' '~'
+L 'L' ''' 'l' 'L' ''' '`'
+M 'M' '>' 'm' 'M' '>' 0x00
+N 'N' '<' 'n' 'N' '<' 0x0303
+O 'O' '(' 'o' 'O' '(' 0x00
+P 'P' ')' 'p' 'P' ')' 0x00
+Q 'Q' '*' 'q' 'Q' '*' 0x0300
+R 'R' '3' 'r' 'R' '3' 0x20AC
+S 'S' '4' 's' 'S' '4' 0x00DF
+T 'T' '+' 't' 'T' '+' 0x00A3
+U 'U' '&' 'u' 'U' '&' 0x0308
+V 'V' '9' 'v' 'V' '9' '^'
+W 'W' '1' 'w' 'W' '1' 0x00
+X 'X' '7' 'x' 'X' '7' 0xEF00
+Y 'Y' '!' 'y' 'Y' '!' 0x00A1
+Z 'Z' '#' 'z' 'Z' '#' 0x00
+
+COMMA ',' ',' ',' ';' ';' '|'
+PERIOD '.' '.' '.' ':' ':' 0x2026
+AT '@' '0' '@' '0' '0' 0x2022
+SLASH '/' '/' '/' '?' '?' '\'
+
+SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
+ENTER 0xa 0xa 0xa 0xa 0xa 0xa
+
+# on pc keyboards
+TAB 0x9 0x9 0x9 0x9 0x9 0x9
+0 '0' '0' '0' ')' ')' ')'
+1 '1' '1' '1' '!' '!' '!'
+2 '2' '2' '2' '@' '@' '@'
+3 '3' '3' '3' '#' '#' '#'
+4 '4' '4' '4' '$' '$' '$'
+5 '5' '5' '5' '%' '%' '%'
+6 '6' '6' '6' '^' '^' '^'
+7 '7' '7' '7' '&' '&' '&'
+8 '8' '8' '8' '*' '*' '*'
+9 '9' '9' '9' '(' '(' '('
+
+GRAVE '`' '`' '`' '~' '`' '~'
+MINUS '-' '-' '-' '_' '-' '_'
+EQUALS '=' '=' '=' '+' '=' '+'
+LEFT_BRACKET '[' '[' '[' '{' '[' '{'
+RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
+BACKSLASH '\' '\' '\' '|' '\' '|'
+SEMICOLON ';' ';' ';' ':' ';' ':'
+APOSTROPHE ''' ''' ''' '"' ''' '"'
+STAR '*' '*' '*' '*' '*' '*'
+POUND '#' '#' '#' '#' '#' '#'
+PLUS '+' '+' '+' '+' '+' '+'
+
+
+
diff --git a/target/board/emulator/tuttle2.kl b/target/board/emulator/tuttle2.kl
new file mode 100644
index 000000000..a48a5ab04
--- /dev/null
+++ b/target/board/emulator/tuttle2.kl
@@ -0,0 +1,74 @@
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 158 BACK WAKE_DROPPED
+key 230 SOFT_RIGHT WAKE
+key 60 SOFT_RIGHT WAKE
+key 107 ENDCALL WAKE_DROPPED
+key 62 ENDCALL WAKE_DROPPED
+key 229 MENU WAKE_DROPPED
+key 59 MENU WAKE_DROPPED
+key 228 POUND
+key 227 STAR
+key 231 CALL WAKE_DROPPED
+key 61 CALL WAKE_DROPPED
+key 232 DPAD_CENTER WAKE_DROPPED
+key 108 DPAD_DOWN WAKE_DROPPED
+key 103 DPAD_UP WAKE_DROPPED
+key 102 HOME WAKE
+key 105 DPAD_LEFT WAKE_DROPPED
+key 106 DPAD_RIGHT WAKE_DROPPED
+key 115 VOLUME_UP
+key 114 VOLUME_DOWN
+key 116 POWER WAKE
+key 212 SLASH
+
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 14 DEL
+
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 28 ENTER
+
+key 56 ALT_LEFT
+key 42 SHIFT_LEFT
+key 215 AT
+key 57 SPACE
+key 53 SLASH
+key 127 SYM
+key 100 ALT_RIGHT
+
+key 399 GRAVE
diff --git a/target/board/generic/AndroidBoard.mk b/target/board/generic/AndroidBoard.mk
new file mode 100644
index 000000000..09badee23
--- /dev/null
+++ b/target/board/generic/AndroidBoard.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+file := $(TARGET_OUT_KEYLAYOUT)/tuttle2.kl
+ALL_PREBUILT += $(file)
+$(file) : $(LOCAL_PATH)/tuttle2.kl | $(ACP)
+ $(transform-prebuilt-to-target)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := tuttle2.kcm
+include $(BUILD_KEY_CHAR_MAP)
diff --git a/target/board/generic/BoardConfig.mk b/target/board/generic/BoardConfig.mk
new file mode 100644
index 000000000..a8747420c
--- /dev/null
+++ b/target/board/generic/BoardConfig.mk
@@ -0,0 +1,11 @@
+# config.mk
+#
+# Product-specific compile-time definitions.
+#
+
+# The generic product target doesn't have any hardware-specific pieces.
+TARGET_NO_BOOTLOADER := true
+TARGET_NO_KERNEL := true
+TARGET_NO_RADIOIMAGE := true
+HAVE_HTC_AUDIO_DRIVER := true
+BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/board/generic/README.txt b/target/board/generic/README.txt
new file mode 100644
index 000000000..ddac68e78
--- /dev/null
+++ b/target/board/generic/README.txt
@@ -0,0 +1,9 @@
+The "generic" product defines a non-hardware-specific target
+without a kernel or bootloader.
+
+It can be used to build the entire user-level system, and
+will work with the emulator, though sound will not work
+(see the "emulator" product for that).
+
+It is not a product "base class"; no other products inherit
+from it or use it in any way.
diff --git a/target/board/generic/system.prop b/target/board/generic/system.prop
new file mode 100644
index 000000000..f2424c970
--- /dev/null
+++ b/target/board/generic/system.prop
@@ -0,0 +1,6 @@
+#
+# system.prop for generic sdk
+#
+
+rild.libpath=/system/lib/libreference-ril.so
+rild.libargs=-d /dev/ttyS0
diff --git a/target/board/generic/tuttle2.kcm b/target/board/generic/tuttle2.kcm
new file mode 100644
index 000000000..0a2dd8c99
--- /dev/null
+++ b/target/board/generic/tuttle2.kcm
@@ -0,0 +1,66 @@
+[type=QWERTY]
+
+# keycode display number base caps fn caps_fn
+
+A 'A' '%' 'a' 'A' '%' 0x00
+B 'B' '=' 'b' 'B' '=' 0x00
+C 'C' '8' 'c' 'C' '8' 0x00E7
+D 'D' '5' 'd' 'D' '5' 0x00
+E 'E' '2' 'e' 'E' '2' 0x0301
+F 'F' '6' 'f' 'F' '6' 0x00A5
+G 'G' '-' 'g' 'G' '-' '_'
+H 'H' '[' 'h' 'H' '[' '{'
+I 'I' '$' 'i' 'I' '$' 0x0302
+J 'J' ']' 'j' 'J' ']' '}'
+K 'K' '"' 'k' 'K' '"' '~'
+L 'L' ''' 'l' 'L' ''' '`'
+M 'M' '>' 'm' 'M' '>' 0x00
+N 'N' '<' 'n' 'N' '<' 0x0303
+O 'O' '(' 'o' 'O' '(' 0x00
+P 'P' ')' 'p' 'P' ')' 0x00
+Q 'Q' '*' 'q' 'Q' '*' 0x0300
+R 'R' '3' 'r' 'R' '3' 0x20AC
+S 'S' '4' 's' 'S' '4' 0x00DF
+T 'T' '+' 't' 'T' '+' 0x00A3
+U 'U' '&' 'u' 'U' '&' 0x0308
+V 'V' '9' 'v' 'V' '9' '^'
+W 'W' '1' 'w' 'W' '1' 0x00
+X 'X' '7' 'x' 'X' '7' 0xEF00
+Y 'Y' '!' 'y' 'Y' '!' 0x00A1
+Z 'Z' '#' 'z' 'Z' '#' 0x00
+
+COMMA ',' ',' ',' ';' ';' '|'
+PERIOD '.' '.' '.' ':' ':' 0x2026
+AT '@' '0' '@' '0' '0' 0x2022
+SLASH '/' '/' '/' '?' '?' '\'
+
+SPACE 0x20 0x20 0x20 0x20 0xEF01 0xEF01
+ENTER 0xa 0xa 0xa 0xa 0xa 0xa
+
+# on pc keyboards
+TAB 0x9 0x9 0x9 0x9 0x9 0x9
+0 '0' '0' '0' ')' ')' ')'
+1 '1' '1' '1' '!' '!' '!'
+2 '2' '2' '2' '@' '@' '@'
+3 '3' '3' '3' '#' '#' '#'
+4 '4' '4' '4' '$' '$' '$'
+5 '5' '5' '5' '%' '%' '%'
+6 '6' '6' '6' '^' '^' '^'
+7 '7' '7' '7' '&' '&' '&'
+8 '8' '8' '8' '*' '*' '*'
+9 '9' '9' '9' '(' '(' '('
+
+GRAVE '`' '`' '`' '~' '`' '~'
+MINUS '-' '-' '-' '_' '-' '_'
+EQUALS '=' '=' '=' '+' '=' '+'
+LEFT_BRACKET '[' '[' '[' '{' '[' '{'
+RIGHT_BRACKET ']' ']' ']' '}' ']' '}'
+BACKSLASH '\' '\' '\' '|' '\' '|'
+SEMICOLON ';' ';' ';' ':' ';' ':'
+APOSTROPHE ''' ''' ''' '"' ''' '"'
+STAR '*' '*' '*' '*' '*' '*'
+POUND '#' '#' '#' '#' '#' '#'
+PLUS '+' '+' '+' '+' '+' '+'
+
+
+
diff --git a/target/board/generic/tuttle2.kl b/target/board/generic/tuttle2.kl
new file mode 100644
index 000000000..a78a6eb51
--- /dev/null
+++ b/target/board/generic/tuttle2.kl
@@ -0,0 +1,74 @@
+key 2 1
+key 3 2
+key 4 3
+key 5 4
+key 6 5
+key 7 6
+key 8 7
+key 9 8
+key 10 9
+key 11 0
+key 158 BACK WAKE_DROPPED
+key 230 SOFT_RIGHT WAKE
+key 60 SOFT_RIGHT WAKE
+key 107 ENDCALL WAKE_DROPPED
+key 62 ENDCALL WAKE_DROPPED
+key 229 MENU WAKE_DROPPED
+key 59 MENU WAKE_DROPPED
+key 228 POUND
+key 227 STAR
+key 231 CALL WAKE_DROPPED
+key 61 CALL WAKE_DROPPED
+key 232 DPAD_CENTER WAKE_DROPPED
+key 108 DPAD_DOWN WAKE_DROPPED
+key 103 DPAD_UP WAKE_DROPPED
+key 102 HOME WAKE
+key 105 DPAD_LEFT WAKE_DROPPED
+key 106 DPAD_RIGHT WAKE_DROPPED
+key 115 VOLUME_UP
+key 114 VOLUME_DOWN
+key 116 POWER WAKE
+key 212 SLASH
+
+key 16 Q
+key 17 W
+key 18 E
+key 19 R
+key 20 T
+key 21 Y
+key 22 U
+key 23 I
+key 24 O
+key 25 P
+
+key 30 A
+key 31 S
+key 32 D
+key 33 F
+key 34 G
+key 35 H
+key 36 J
+key 37 K
+key 38 L
+key 14 DEL
+
+key 44 Z
+key 45 X
+key 46 C
+key 47 V
+key 48 B
+key 49 N
+key 50 M
+key 51 COMMA
+key 52 PERIOD
+key 28 ENTER
+
+key 56 ALT_LEFT
+key 42 SHIFT_LEFT
+key 215 AT
+key 57 SPACE
+key 53 SLASH
+key 127 SYM
+key 100 ALT_LEFT
+
+key 399 GRAVE
diff --git a/target/board/sim/AndroidBoard.mk b/target/board/sim/AndroidBoard.mk
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/target/board/sim/AndroidBoard.mk
diff --git a/target/board/sim/BoardConfig.mk b/target/board/sim/BoardConfig.mk
new file mode 100644
index 000000000..92679d93a
--- /dev/null
+++ b/target/board/sim/BoardConfig.mk
@@ -0,0 +1,22 @@
+# config.mk
+#
+# Product-specific compile-time definitions.
+#
+
+# Don't try prelinking or compressing the shared libraries
+# used by the simulator. The host OS won't know what to do
+# with them, and they may not even be ELF files.
+#
+# These definitions override the defaults in config/config.make.
+TARGET_COMPRESS_MODULE_SYMBOLS := false
+TARGET_PRELINK_MODULE := false
+
+# Don't try to build a bootloader.
+TARGET_NO_BOOTLOADER := true
+
+# Don't bother with a kernel
+TARGET_NO_KERNEL := true
+
+#the simulator partially emulates the original HTC /dev/eac audio interface
+HAVE_HTC_AUDIO_DRIVER := true
+BOARD_USES_GENERIC_AUDIO := true
diff --git a/target/product/AndroidProducts.mk b/target/product/AndroidProducts.mk
new file mode 100644
index 000000000..1bf3c3f6c
--- /dev/null
+++ b/target/product/AndroidProducts.mk
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# This file should set PRODUCT_MAKEFILES to a list of product makefiles
+# to expose to the build system. LOCAL_DIR will already be set to
+# the directory containing this file.
+#
+# This file may not rely on the value of any variable other than
+# LOCAL_DIR; do not use any conditionals, and do not look up the
+# value of any variable that isn't set in this file or in a file that
+# it includes.
+#
+
+PRODUCT_MAKEFILES := \
+ $(LOCAL_DIR)/generic.mk \
+ $(LOCAL_DIR)/min_dev.mk \
+ $(LOCAL_DIR)/sdk.mk \
+ $(LOCAL_DIR)/sim.mk \
+ $(LOCAL_DIR)/generic_with_google.mk
diff --git a/target/product/core.mk b/target/product/core.mk
new file mode 100644
index 000000000..117bb1123
--- /dev/null
+++ b/target/product/core.mk
@@ -0,0 +1,23 @@
+PRODUCT_BRAND :=
+PRODUCT_NAME :=
+PRODUCT_DEVICE :=
+PRODUCT_POLICY := android.policy_phone
+PRODUCT_PROPERTY_OVERRIDES := \
+ ro.config.notification_sound=F1_New_SMS.ogg
+
+PRODUCT_PACKAGES := \
+ framework-res \
+ Browser \
+ Contacts \
+ Home \
+ HTMLViewer \
+ Phone \
+ ContactsProvider \
+ DownloadProvider \
+ GoogleSearch \
+ MediaProvider \
+ SettingsProvider \
+ TelephonyProvider \
+ UserDictionaryProvider \
+ PackageInstaller \
+ Bugreport
diff --git a/target/product/generic.mk b/target/product/generic.mk
new file mode 100644
index 000000000..b9bc07064
--- /dev/null
+++ b/target/product/generic.mk
@@ -0,0 +1,26 @@
+# This is a generic product that isn't specialized for a specific device.
+# It includes the base Android platform. If you need Google-specific features,
+# you should derive from generic_with_google.mk
+
+PRODUCT_PACKAGES := \
+ AlarmClock \
+ AlarmProvider \
+ Calendar \
+ Camera \
+ DrmProvider \
+ LatinIME \
+ Mms \
+ Music \
+ Settings \
+ Sync \
+ Updater \
+ CalendarProvider \
+ SubscribedFeedsProvider \
+ SyncProvider
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
+
+# Overrides
+PRODUCT_BRAND := generic
+PRODUCT_DEVICE := generic
+PRODUCT_NAME := generic
diff --git a/target/product/generic_with_google.mk b/target/product/generic_with_google.mk
new file mode 100755
index 000000000..af499b3ae
--- /dev/null
+++ b/target/product/generic_with_google.mk
@@ -0,0 +1,13 @@
+# This is a generic product that isn't specialized for a specific device.
+# It includes the base Android platform including some Google-specific features.
+# If you do not want to include Google specific features, you should derive
+# from generic.mk
+
+PRODUCT_PACKAGES := \
+ GoogleContactsProvider \
+ GoogleSubscribedFeedsProvider
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/generic.mk)
+
+# Overrides
+PRODUCT_NAME := generic_with_google
diff --git a/target/product/min_dev.mk b/target/product/min_dev.mk
new file mode 100644
index 000000000..7d0fbe69b
--- /dev/null
+++ b/target/product/min_dev.mk
@@ -0,0 +1,19 @@
+
+PRODUCT_POLICY := android.policy_phone
+PRODUCT_PROPERTY_OVERRIDES := \
+ ro.config.notification_sound=F1_New_SMS.ogg
+PRODUCT_BRAND := generic
+PRODUCT_NAME := min_dev
+PRODUCT_DEVICE := generic
+
+PRODUCT_PACKAGES := \
+ DownloadProvider \
+ GoogleSearch \
+ MediaProvider \
+ SettingsProvider \
+ PackageInstaller \
+ Bugreport \
+ Launcher \
+ Settings \
+ sqlite3
+
diff --git a/target/product/sdk.mk b/target/product/sdk.mk
new file mode 100644
index 000000000..e8109e51b
--- /dev/null
+++ b/target/product/sdk.mk
@@ -0,0 +1,29 @@
+PRODUCT_PROPERTY_OVERRIDES :=
+
+PRODUCT_PACKAGES := \
+ AlarmClock \
+ ApiDemos \
+ Camera \
+ Development \
+ DrmProvider \
+ Email \
+ Fallback \
+ GPSEnable \
+ Launcher \
+ Maps \
+ Music \
+ Mms \
+ Settings \
+ SdkSetup \
+ CustomLocale \
+ gpstest \
+ sqlite3 \
+ SoftKeyboard
+
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/core.mk)
+
+# Overrides
+PRODUCT_BRAND := generic
+PRODUCT_NAME := sdk
+PRODUCT_DEVICE := generic
diff --git a/target/product/security/README b/target/product/security/README
new file mode 100644
index 000000000..b92693d15
--- /dev/null
+++ b/target/product/security/README
@@ -0,0 +1,38 @@
+The following commands were used to generate the test key pair:
+
+ openssl genrsa -3 -out testkey.pem 2048
+
+ openssl req -new -x509 -key testkey.pem -out testkey.x509.pem -days 10000 \
+ -subj '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
+
+ openssl pkcs8 -in testkey.pem -topk8 -outform DER -out testkey.pk8 -nocrypt
+
+Alternatively you can use the "mkkey.sh" command included in this directory.
+
+The following standard test keys are currently included:
+
+testkey -- a generic key for packages that do not otherwise specify a key.
+platform -- a test key for packages that are part of the core platform.
+shared -- a test key for things that are shared in the home/contacts process.
+media -- a test key for packages that are part of the media/download system.
+
+These test keys are used strictly in development, and should never be assumed
+to convey any sort of validity. When $BUILD_SECURE=true, the code should not
+honor these keys in any context.
+
+
+signing using the openssl commandline (for boot/system images)
+--------------------------------------------------------------
+
+1. convert pk8 format key to pem format
+ % openssl pkcs8 -inform DER -nocrypt -in testkey.pk8 -out testkey.pem
+
+2. create a signature using the pem format key
+ % openssl dgst -binary -sha1 -sign testkey.pem FILE > FILE.sig
+
+extracting public keys for embedding
+------------------------------------
+it's a Java tool
+but it generates C code
+take a look at commands/recovery/Android.mk
+you'll see it running $(HOST_OUT_JAVA_LIBRARIES)/dumpkey.jar \ No newline at end of file
diff --git a/target/product/security/media.pk8 b/target/product/security/media.pk8
new file mode 100644
index 000000000..a6db9ba17
--- /dev/null
+++ b/target/product/security/media.pk8
Binary files differ
diff --git a/target/product/security/media.x509.pem b/target/product/security/media.x509.pem
new file mode 100644
index 000000000..98cd443db
--- /dev/null
+++ b/target/product/security/media.x509.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEqDCCA5CgAwIBAgIJAPK5jmEjVyxOMA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0wODA0MTUyMzQwNTdaFw0zNTA5MDEyMzQwNTdaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBAK4lDFoW75f8KGmsZRsyF8w2ug6GlkFo1YoE
+n0DOhYZxI6P/tPbZScM88to6BcI+rKpX2AOImxdZvPWefG8hiQriUIW37VaqYmwJ
+ie+czTY2LKDo0blgP9TYModnkmzMCQxot3Wuf/MJNMw2nvKFWiZn3wxmf9DHz12O
+umVYBnNzA7tiRybquu37cvB+16dqs8uaOBxLfc2AmxQNiR8AITvkAfWNagamHq3D
+qcLxxlZyhbCa4JNCpm+kIer5Ot91c6AowzHXBgGrOvfMhAM+znx3KjpbhrDb6dd3
+w6SKqYAe3O4ngVifRNnkETl5YAV2qZQQuoEJElna2YxsaP94S48CAQOjgfwwgfkw
+HQYDVR0OBBYEFMopPKqLwO0+VC7vQgWiv/K1fk11MIHJBgNVHSMEgcEwgb6AFMop
+PKqLwO0+VC7vQgWiv/K1fk11oYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPK5jmEjVyxOMAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAITelRbV5KhyF6c9qEhwSPUzc6X3
+M/OQ1hvfPMnlJRYlv8qnwxWcriddFyqa4eh21UWBJ6xUL2gpDdUQwAKdj1Hg7hVr
+e3tazbOUJBuOx4t05cQsXK+uFWyvW9GZojonUk2gct6743hGSlM2MLDk0P+34I7L
+cB+ttjecdEZ/bgDG7YiFlTgHkgOHVgB4csjjAHr0I6V6LKs6KChptkxLe9X8GH0K
+fiQVll1ark4Hpt91G0p16Xk8kYphK4HNC2KK7gFo3ETkexDTWTJghJ1q321yfcJE
+RMIh0/nsw2jK0HmZ8rgQW8HyDTjUEGbMFBHCV6lupDSfV0ZWVQfk6AIKGoE=
+-----END CERTIFICATE-----
diff --git a/target/product/security/mkkey.sh b/target/product/security/mkkey.sh
new file mode 100644
index 000000000..86744f6af
--- /dev/null
+++ b/target/product/security/mkkey.sh
@@ -0,0 +1,15 @@
+if ["$1" == ""]; then
+ echo "Create a test certificate key."
+ echo "Usage: $0 NAME"
+ echo "Will generate NAME.pk8 and NAME.x509.pem"
+ echo " /C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com"
+ return
+fi
+
+openssl genrsa -3 -out $1.pem 2048
+
+openssl req -new -x509 -key $1.pem -out $1.x509.pem -days 10000 \
+ -subj '/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
+
+openssl pkcs8 -in $1.pem -topk8 -outform DER -out $1.pk8 -nocrypt
+
diff --git a/target/product/security/platform.pk8 b/target/product/security/platform.pk8
new file mode 100644
index 000000000..e27a3933e
--- /dev/null
+++ b/target/product/security/platform.pk8
Binary files differ
diff --git a/target/product/security/platform.x509.pem b/target/product/security/platform.x509.pem
new file mode 100644
index 000000000..087f02e6a
--- /dev/null
+++ b/target/product/security/platform.x509.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEqDCCA5CgAwIBAgIJALOZgIbQVs/6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0wODA0MTUyMjQwNTBaFw0zNTA5MDEyMjQwNTBaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBAJx4BZKsDV04HN6qZezIpgBuNkgMbXIHsSAR
+vlCGOqvitV0Amt9xRtbyICKAx81Ne9smJDuKgGwms0sTdSOkkmgiSQTcAUk+fArP
+GgXIdPabA3tgMJ2QdNJCgOFrrSqHNDYZUer3KkgtCbIEsYdeEqyYwap3PWgAuer9
+5W1Yvtjo2hb5o2AJnDeoNKbf7be2tEoEngeiafzPLFSW8s821k35CjuNjzSjuqtM
+9TNxqydxmzulh1StDFP8FOHbRdUeI0+76TybpO35zlQmE1DsU1YHv2mi/0qgfbX3
+6iANCabBtJ4hQC+J7RGQiTqrWpGA8VLoL4WkV1PPX8GQccXuyCcCAQOjgfwwgfkw
+HQYDVR0OBBYEFE/koLPdnLop9x1yh8Tnw48ghsKZMIHJBgNVHSMEgcEwgb6AFE/k
+oLPdnLop9x1yh8Tnw48ghsKZoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJALOZgIbQVs/6MAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFclUbjZOh9z3g9tRp+G2tZwFAAp
+PIigzXzXeLc9r8wZf6t25iEuVsHHYc/EL9cz3lLFCuCIFM78CjtaGkNGBU2Cnx2C
+tCsgSL+ItdFJKe+F9g7dEtctVWV+IuPoXQTIMdYT0Zk4u4mCJH+jISVroS0dao+S
+6h2xw3Mxe6DAN/DRr/ZFrvIkl5+6bnoUvAJccbmBOM7z3fwFlhfPJIRc97QNY4L3
+J17XOElatuWTG5QhdlxJG3L7aOCA29tYwgKdNHyLMozkPvaosVUz7fvpib1qSN1L
+IC7alMarjdW4OZID2q4u1EYjLk/pvZYTlMYwDlE448/Shebk5INTjLixs1c=
+-----END CERTIFICATE-----
diff --git a/target/product/security/shared.pk8 b/target/product/security/shared.pk8
new file mode 100644
index 000000000..cf99acd52
--- /dev/null
+++ b/target/product/security/shared.pk8
Binary files differ
diff --git a/target/product/security/shared.x509.pem b/target/product/security/shared.x509.pem
new file mode 100644
index 000000000..7f886a844
--- /dev/null
+++ b/target/product/security/shared.x509.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEqDCCA5CgAwIBAgIJAPKnM5a9OHZ6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0wODA3MjMyMTU3NTlaFw0zNTEyMDkyMTU3NTlaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBAMjC2/0JSi30XD/xoy7SGAXscvxY0BeXG9D2
+tSwmLXCBnRkZZ+FY39Oix/Gz4OgM5UXXnShIIgIR64bw/YMS03tCDBE3UMyUYYro
+cvSIZGO9xGJ8qgwEg8hkk+NRVXEXAzi/3MTNat3RwKLzX1zyTtPkBDo+WOKwXmZM
+zeEry2dzX9bfEknDaeYlQrwKRynlORf1w4/6UtF7c8nHN5jdsY7UgVkIdVR+Zr/F
+2spMJabrlg7ZaSNwnaMCumRstJazJehsXIsuejN3srvkx88zJUKRFj9okVKsCIVQ
+yDxQj0v1rfCu1aLcoFg/mrCtF2UNt+6ksj/bRYhVR9D+q3IYOIkCAQOjgfwwgfkw
+HQYDVR0OBBYEFMtMfizbs/CtqY2reZaNFy6dux7RMIHJBgNVHSMEgcEwgb6AFMtM
+fizbs/CtqY2reZaNFy6dux7RoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAPKnM5a9OHZ6MAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAECo0JaZeVnpF6NsRCRra6wrrgVD
+fs2JeUEY94NHIDUtHG+KObCGmUL02mWYH6opUdM5cRKewZIdeVZxxSfW4knyUoKf
+r1tZExAxHi3gllANVorUEUplbcNKjG9hBFOvwep5ktukqns/hUOm41wHKN53/pfu
+rIN3H9DskPjkRJQ07gtgRXg+cMei5GAkkmDgA892CNw1Kkye9wbe9LJgUOl4ri//
+16MyN4cBSRXrPMh0/MeprpMId8XIx9HC4qjuhjyJGA0YVc7bpADnukPMyqckPTl+
+fA6Ojk19T5K2u+rUnAzwGAae3coufi+0Zo2J2715UNDNJUGA+h6q/CpVb4Q=
+-----END CERTIFICATE-----
diff --git a/target/product/security/testkey.pk8 b/target/product/security/testkey.pk8
new file mode 100644
index 000000000..586c1bd5c
--- /dev/null
+++ b/target/product/security/testkey.pk8
Binary files differ
diff --git a/target/product/security/testkey.x509.pem b/target/product/security/testkey.x509.pem
new file mode 100644
index 000000000..e242d83e2
--- /dev/null
+++ b/target/product/security/testkey.x509.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
+VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
+VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
+AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
+Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET
+MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
+A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
+ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
+hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM
+qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4
+wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy
+4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU
+RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s
+zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw
+HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ
+AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
+CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
+QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
+CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud
+EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa
+J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y
+LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe
++ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX
+31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr
+sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=
+-----END CERTIFICATE-----
diff --git a/target/product/sim.mk b/target/product/sim.mk
new file mode 100644
index 000000000..7b274956b
--- /dev/null
+++ b/target/product/sim.mk
@@ -0,0 +1,9 @@
+PRODUCT_PACKAGES := \
+ IM
+
+$(call inherit-product, $(SRC_TARGET_DIR)/product/generic_with_google.mk)
+
+# Overrides
+PRODUCT_NAME := sim
+PRODUCT_DEVICE := sim
+PRODUCT_LOCALES := en_US
diff --git a/tools/acp/Android.mk b/tools/acp/Android.mk
new file mode 100644
index 000000000..5e0e2e461
--- /dev/null
+++ b/tools/acp/Android.mk
@@ -0,0 +1,26 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Custom version of cp.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ acp.c
+
+ifeq ($(HOST_OS),cygwin)
+LOCAL_CFLAGS += -DWIN32_EXE
+endif
+ifeq ($(HOST_OS),darwin)
+LOCAL_CFLAGS += -DMACOSX_RSRC
+endif
+ifeq ($(HOST_OS),linux)
+endif
+
+LOCAL_STATIC_LIBRARIES := libhost
+LOCAL_C_INCLUDES := build/libs/host/include
+LOCAL_MODULE := acp
+LOCAL_ACP_UNAVAILABLE := true
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/acp/README b/tools/acp/README
new file mode 100644
index 000000000..a1809d928
--- /dev/null
+++ b/tools/acp/README
@@ -0,0 +1,40 @@
+README for Android "acp" Command
+
+The "cp" command was judged and found wanting. The issues are:
+
+Mac OS X:
+ - Uses the BSD cp, not the fancy GNU cp. It lacks the "-u" flag, which
+ only copies files if they are newer than the destination. This can
+ slow the build when copying lots of content.
+ - Doesn't take the "-d" flag, which causes symlinks to be copied as
+ links. This is the default behavior, so it's not all bad, but it
+ complains if you supply "-d".
+
+MinGW/Cygwin:
+ - Gets really weird when copying a file called "foo.exe", failing with
+ "cp: skipping file 'foo.exe', as it was replaced while being copied".
+ This only seems to happen when the source file is on an NFS/Samba
+ volume. "cp" works okay copying from local disk.
+
+Linux:
+ - On some systems it's possible to have microsecond-accurate timestamps
+ on an NFS volume, and non-microsecond timestamps on a local volume.
+ If you copy from NFS to local disk, your NFS files will always be
+ newer, because the local disk time stamp is truncated rather than
+ rounded up. This foils the "-u" flag if you also supply the "-p" flag
+ to preserve timestamps.
+ - The Darwin linker insists that ranlib be current. If you copy the
+ library, the time stamp no longer matches. Preserving the time
+ stamp is essential, so simply turning the "-p" flag off doesn't work.
+
+Futzing around these in make with GNU make functions is awkward at best.
+It's easier and more reliable to write a cp command that works properly.
+
+
+The "acp" command takes most of the standard flags, following the GNU
+conventions. It adds a "-e" flag, used when copying executables around.
+On most systems it is ignored, but on MinGW/Cygwin it allows "cp foo bar"
+to work when what is actually meant is "cp foo.exe bar.exe". Unlike the
+default Cygwin cp, "acp foo bar" will not find foo.exe unless you add
+the "-e" flag, avoiding potential ambiguity.
+
diff --git a/tools/acp/acp.c b/tools/acp/acp.c
new file mode 100644
index 000000000..eb1de1f96
--- /dev/null
+++ b/tools/acp/acp.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2005 The Android Open Source Project
+ *
+ * Android "cp" replacement.
+ *
+ * The GNU/Linux "cp" uses O_LARGEFILE in its open() calls, utimes() instead
+ * of utime(), and getxattr()/setxattr() instead of chmod(). These are
+ * probably "better", but are non-portable, and not necessary for our
+ * purposes.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <utime.h>
+#include <limits.h>
+#include <errno.h>
+#include <assert.h>
+#include <host/CopyFile.h>
+
+/*#define DEBUG_MSGS*/
+#ifdef DEBUG_MSGS
+# define DBUG(x) printf x
+#else
+# define DBUG(x) ((void)0)
+#endif
+
+#define FSSEP '/' /* filename separator char */
+
+
+/*
+ * Process the command-line file arguments.
+ *
+ * Returns 0 on success.
+ */
+int process(int argc, char* const argv[], unsigned int options)
+{
+ int retVal = 0;
+ int i, cc;
+ char* stripDest = NULL;
+ int stripDestLen;
+ struct stat destStat;
+ bool destMustBeDir = false;
+ struct stat sb;
+
+ assert(argc >= 2);
+
+ /*
+ * Check for and trim a trailing slash on the last arg.
+ *
+ * It's useful to be able to say "cp foo bar/" when you want to copy
+ * a single file into a directory. If you say "cp foo bar", and "bar"
+ * does not exist, it will create "bar", when what you really wanted
+ * was for the cp command to fail with "directory does not exist".
+ */
+ stripDestLen = strlen(argv[argc-1]);
+ stripDest = malloc(stripDestLen+1);
+ memcpy(stripDest, argv[argc-1], stripDestLen+1);
+ if (stripDest[stripDestLen-1] == FSSEP) {
+ stripDest[--stripDestLen] = '\0';
+ destMustBeDir = true;
+ }
+
+ if (argc > 2)
+ destMustBeDir = true;
+
+ /*
+ * Start with a quick check to ensure that, if we're expecting to copy
+ * to a directory, the target already exists and is actually a directory.
+ * It's okay if it's a symlink to a directory.
+ *
+ * If it turns out to be a directory, go ahead and raise the
+ * destMustBeDir flag so we do some path concatenation below.
+ */
+ if (stat(stripDest, &sb) < 0) {
+ if (destMustBeDir) {
+ if (errno == ENOENT)
+ fprintf(stderr,
+ "acp: destination directory '%s' does not exist\n",
+ stripDest);
+ else
+ fprintf(stderr, "acp: unable to stat dest dir\n");
+ retVal = 1;
+ goto bail;
+ }
+ } else {
+ if (S_ISDIR(sb.st_mode)) {
+ DBUG(("--- dest exists and is a dir, setting flag\n"));
+ destMustBeDir = true;
+ } else if (destMustBeDir) {
+ fprintf(stderr,
+ "acp: destination '%s' is not a directory\n",
+ stripDest);
+ retVal = 1;
+ goto bail;
+ }
+ }
+
+ /*
+ * Copying files.
+ *
+ * Strip trailing slashes off. They shouldn't be there, but
+ * sometimes file completion will put them in for directories.
+ *
+ * The observed behavior of GNU and BSD cp is that they print warnings
+ * if something fails, but continue on. If any part fails, the command
+ * exits with an error status.
+ */
+ for (i = 0; i < argc-1; i++) {
+ const char* srcName;
+ char* src;
+ char* dst;
+ int copyResult;
+ int srcLen;
+
+ /* make a copy of the source name, and strip trailing '/' */
+ srcLen = strlen(argv[i]);
+ src = malloc(srcLen+1);
+ memcpy(src, argv[i], srcLen+1);
+
+ if (src[srcLen-1] == FSSEP)
+ src[--srcLen] = '\0';
+
+ /* find just the name part */
+ srcName = strrchr(src, FSSEP);
+ if (srcName == NULL) {
+ srcName = src;
+ } else {
+ srcName++;
+ assert(*srcName != '\0');
+ }
+
+ if (destMustBeDir) {
+ /* concatenate dest dir and src name */
+ int srcNameLen = strlen(srcName);
+
+ dst = malloc(stripDestLen +1 + srcNameLen +1);
+ memcpy(dst, stripDest, stripDestLen);
+ dst[stripDestLen] = FSSEP;
+ memcpy(dst + stripDestLen+1, srcName, srcNameLen+1);
+ } else {
+ /* simple */
+ dst = stripDest;
+ }
+
+ /*
+ * Copy the source to the destination.
+ */
+ copyResult = copyFile(src, dst, options);
+
+ if (copyResult != 0)
+ retVal = 1;
+
+ free(src);
+ if (dst != stripDest)
+ free(dst);
+ }
+
+bail:
+ free(stripDest);
+ return retVal;
+}
+
+/*
+ * Set up the options.
+ */
+int main(int argc, char* const argv[])
+{
+ bool wantUsage;
+ int ic, retVal;
+ int verboseLevel;
+ unsigned int options;
+
+ verboseLevel = 0;
+ options = 0;
+ wantUsage = false;
+
+ while (1) {
+ ic = getopt(argc, argv, "defprtuv");
+ if (ic < 0)
+ break;
+
+ switch (ic) {
+ case 'd':
+ options |= COPY_NO_DEREFERENCE;
+ break;
+ case 'e':
+ options |= COPY_TRY_EXE;
+ break;
+ case 'f':
+ options |= COPY_FORCE;
+ break;
+ case 'p':
+ options |= COPY_PERMISSIONS;
+ break;
+ case 't':
+ options |= COPY_TIMESTAMPS;
+ break;
+ case 'r':
+ options |= COPY_RECURSIVE;
+ break;
+ case 'u':
+ options |= COPY_UPDATE_ONLY;
+ break;
+ case 'v':
+ verboseLevel++;
+ break;
+ default:
+ fprintf(stderr, "Unexpected arg -%c\n", ic);
+ wantUsage = true;
+ break;
+ }
+
+ if (wantUsage)
+ break;
+ }
+
+ options |= verboseLevel & COPY_VERBOSE_MASK;
+
+ if (optind == argc-1) {
+ fprintf(stderr, "acp: missing destination file\n");
+ return 2;
+ } else if (optind+2 > argc)
+ wantUsage = true;
+
+ if (wantUsage) {
+ fprintf(stderr, "Usage: acp [OPTION]... SOURCE DEST\n");
+ fprintf(stderr, " or: acp [OPTION]... SOURCE... DIRECTORY\n");
+ fprintf(stderr, "\nOptions:\n");
+ fprintf(stderr, " -d never follow (dereference) symbolic links\n");
+ fprintf(stderr, " -e if source file doesn't exist, try adding "
+ "'.exe' [Win32 only]\n");
+ fprintf(stderr, " -f use force, removing existing file if it's "
+ "not writeable\n");
+ fprintf(stderr, " -p preserve mode, ownership\n");
+ fprintf(stderr, " -r recursive copy\n");
+ fprintf(stderr, " -t preserve timestamps\n");
+ fprintf(stderr, " -u update only: don't copy if dest is newer\n");
+ fprintf(stderr, " -v verbose output (-vv is more verbose)\n");
+ return 2;
+ }
+
+ retVal = process(argc-optind, argv+optind, options);
+ DBUG(("EXIT: %d\n", retVal));
+ return retVal;
+}
+
diff --git a/tools/apicheck/Android.mk b/tools/apicheck/Android.mk
new file mode 100644
index 000000000..a2ff8a282
--- /dev/null
+++ b/tools/apicheck/Android.mk
@@ -0,0 +1,44 @@
+# Copyright (C) 2007-2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+# We use copy-file-to-new-target so that the installed
+# script file's timestamp is at least as new as the
+# .jar file it wraps.
+
+#TODO(dbort): add a template to do this stuff; share with jx
+
+# the hat script
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := apicheck
+
+include $(BUILD_SYSTEM)/base_rules.mk
+
+$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/apicheck$(COMMON_JAVA_PACKAGE_SUFFIX)
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/apicheck | $(ACP)
+ @echo "Copy: $(PRIVATE_MODULE) ($@)"
+ $(copy-file-to-new-target)
+ $(hide) chmod 755 $@
+
+# the other stuff
+# ============================================================
+subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \
+ src \
+ ))
+
+include $(subdirs)
diff --git a/tools/apicheck/etc/apicheck b/tools/apicheck/etc/apicheck
new file mode 100644
index 000000000..9c00e2572
--- /dev/null
+++ b/tools/apicheck/etc/apicheck
@@ -0,0 +1,46 @@
+#!/bin/bash
+#
+# Copyright (C) 2005, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+prog="$0"
+while [ -h "${prog}" ]; do
+ newProg=`/bin/ls -ld "${prog}"`
+ newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+ if expr "x${newProg}" : 'x/' >/dev/null; then
+ prog="${newProg}"
+ else
+ progdir=`dirname "${prog}"`
+ prog="${progdir}/${newProg}"
+ fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+ opt=`expr "$1" : '-J\(.*\)'`
+ javaOpts="${javaOpts} -${opt}"
+ shift
+done
+
+exec java $javaOpts -jar $libdir/apicheck.jar "$@"
diff --git a/tools/apicheck/src/Android.mk b/tools/apicheck/src/Android.mk
new file mode 100644
index 000000000..c4e7c6e84
--- /dev/null
+++ b/tools/apicheck/src/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+
+# apicheck java library
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
+LOCAL_MODULE:= apicheck
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
diff --git a/tools/apicheck/src/MANIFEST.mf b/tools/apicheck/src/MANIFEST.mf
new file mode 100644
index 000000000..e6dc263be
--- /dev/null
+++ b/tools/apicheck/src/MANIFEST.mf
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+Main-Class: com.android.apicheck.ApiCheck
diff --git a/tools/apicheck/src/com/android/apicheck/AbstractMethodInfo.java b/tools/apicheck/src/com/android/apicheck/AbstractMethodInfo.java
new file mode 100644
index 000000000..ca90820bb
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/AbstractMethodInfo.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+public interface AbstractMethodInfo {
+
+ public void addException(String exec);
+ public void addParameter(ParameterInfo p);
+
+}
diff --git a/tools/apicheck/src/com/android/apicheck/ApiCheck.java b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
new file mode 100644
index 000000000..f78117cd5
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/ApiCheck.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+import org.xml.sax.*;
+import org.xml.sax.helpers.*;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Stack;
+
+public class ApiCheck {
+ // parse out and consume the -whatever command line flags
+ private static ArrayList<String[]> parseFlags(ArrayList<String> allArgs) {
+ ArrayList<String[]> ret = new ArrayList<String[]>();
+
+ int i;
+ for (i = 0; i < allArgs.size(); i++) {
+ // flags with one value attached
+ String flag = allArgs.get(i);
+ if (flag.equals("-error")
+ || flag.equals("-warning")
+ || flag.equals("-hide")) {
+ String[] arg = new String[2];
+ arg[0] = flag;
+ arg[1] = allArgs.get(++i);
+ ret.add(arg);
+ } else {
+ // we've consumed all of the -whatever args, so we're done
+ break;
+ }
+ }
+
+ // i now points to the first non-flag arg; strip what came before
+ for (; i > 0; i--) {
+ allArgs.remove(0);
+ }
+ return ret;
+ }
+
+ public static void main(String[] originalArgs) {
+ // translate to an ArrayList<String> for munging
+ ArrayList<String> args = new ArrayList<String>(originalArgs.length);
+ for (String a: originalArgs) {
+ args.add(a);
+ }
+
+ ArrayList<String[]> flags = ApiCheck.parseFlags(args);
+ for (String[] a: flags) {
+ if (a[0].equals("-error") || a[0].equals("-warning")
+ || a[0].equals("-hide")) {
+ try {
+ int level = -1;
+ if (a[0].equals("-error")) {
+ level = Errors.ERROR;
+ }
+ else if (a[0].equals("-warning")) {
+ level = Errors.WARNING;
+ }
+ else if (a[0].equals("-hide")) {
+ level = Errors.HIDDEN;
+ }
+ Errors.setErrorLevel(Integer.parseInt(a[1]), level);
+ }
+ catch (NumberFormatException e) {
+ System.err.println("Bad argument: " + a[0] + " " + a[1]);
+ System.exit(2);
+ }
+ }
+ }
+
+ String xmlFileName = args.get(0);
+ String xmlFileNameNew = args.get(1);
+ XMLReader xmlreader = null;
+ try {
+ // parse the XML files into our data structures
+ xmlreader = XMLReaderFactory.createXMLReader();
+ ApiCheck acheck = new ApiCheck();
+ MakeHandler handler = acheck.new MakeHandler();
+ xmlreader.setContentHandler(handler);
+ xmlreader.setErrorHandler(handler);
+ FileReader filereader = new FileReader(xmlFileName);
+ xmlreader.parse(new InputSource(filereader));
+ FileReader filereaderNew = new FileReader(xmlFileNameNew);
+ xmlreader.parse(new InputSource(filereaderNew));
+
+ // establish the superclass relationships
+ handler.getOldApi().resolveSuperclasses();
+ handler.getNewApi().resolveSuperclasses();
+
+ // finally, run the consistency check
+ handler.getOldApi().isConsistent(handler.getNewApi());
+
+ } catch (SAXParseException e) {
+ Errors.error(Errors.PARSE_ERROR,
+ new SourcePositionInfo(xmlFileName, e.getLineNumber(), 0),
+ e.getMessage());
+ } catch (Exception e) {
+ e.printStackTrace();
+ Errors.error(Errors.PARSE_ERROR,
+ new SourcePositionInfo(xmlFileName, 0, 0),
+ e.getMessage());
+ }
+
+ Errors.printErrors();
+ System.exit(Errors.hadError ? 1 : 0);
+ }
+
+ private class MakeHandler extends DefaultHandler {
+
+ private Integer mWarningCount;
+ private ApiInfo mOriginalApi;
+ private ApiInfo mNewApi;
+ private boolean mOldApi;
+ private PackageInfo mCurrentPackage;
+ private ClassInfo mCurrentClass;
+ private AbstractMethodInfo mCurrentMethod;
+ private ConstructorInfo mCurrentConstructor;
+ private Stack<ClassInfo> mClassScope = new Stack<ClassInfo>();
+
+
+ public MakeHandler() {
+ super();
+ mOriginalApi = new ApiInfo();
+ mNewApi = new ApiInfo();
+ mOldApi = true;
+
+ }
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) {
+ if (qName.equals("package")) {
+ mCurrentPackage = new PackageInfo(attributes.getValue("name"),
+ SourcePositionInfo.fromXml(attributes.getValue("source")));
+ } else if (qName.equals("class")
+ || qName.equals("interface")) {
+ // push the old outer scope for later recovery, then set
+ // up the new current class object
+ mClassScope.push(mCurrentClass);
+ mCurrentClass = new ClassInfo(attributes.getValue("name"),
+ mCurrentPackage,
+ attributes.getValue("extends") ,
+ qName.equals("interface"),
+ Boolean.valueOf(
+ attributes.getValue("abstract")),
+ Boolean.valueOf(
+ attributes.getValue("static")),
+ Boolean.valueOf(
+ attributes.getValue("final")),
+ attributes.getValue("deprecated"),
+ attributes.getValue("visibility"),
+ SourcePositionInfo.fromXml(attributes.getValue("source")),
+ mCurrentClass);
+ } else if (qName.equals("method")) {
+ mCurrentMethod = new MethodInfo(attributes.getValue("name"),
+ attributes.getValue("return") ,
+ Boolean.valueOf(
+ attributes.getValue("abstract")),
+ Boolean.valueOf(
+ attributes.getValue("native")),
+ Boolean.valueOf(
+ attributes.getValue("synchronized")),
+ Boolean.valueOf(
+ attributes.getValue("static")),
+ Boolean.valueOf(
+ attributes.getValue("final")),
+ attributes.getValue("deprecated"),
+ attributes.getValue("visibility"),
+ SourcePositionInfo.fromXml(attributes.getValue("source")),
+ mCurrentClass);
+ } else if (qName.equals("constructor")) {
+ mCurrentMethod = new ConstructorInfo(attributes.getValue("name"),
+ attributes.getValue("type") ,
+ Boolean.valueOf(
+ attributes.getValue("static")),
+ Boolean.valueOf(
+ attributes.getValue("final")),
+ attributes.getValue("deprecated"),
+ attributes.getValue("visibility"),
+ SourcePositionInfo.fromXml(attributes.getValue("source")),
+ mCurrentClass);
+ } else if (qName.equals("field")) {
+ FieldInfo fInfo = new FieldInfo(attributes.getValue("name"),
+ attributes.getValue("type") ,
+ Boolean.valueOf(
+ attributes.getValue("transient")),
+ Boolean.valueOf(
+ attributes.getValue("volatile")),
+ attributes.getValue("value"),
+ Boolean.valueOf(
+ attributes.getValue("static")),
+ Boolean.valueOf(
+ attributes.getValue("final")),
+ attributes.getValue("deprecated"),
+ attributes.getValue("visibility"),
+ SourcePositionInfo.fromXml(attributes.getValue("source")),
+ mCurrentClass);
+ mCurrentClass.addField(fInfo);
+ } else if (qName.equals("parameter")) {
+ mCurrentMethod.addParameter(new ParameterInfo(attributes.getValue("type"),
+ attributes.getValue("name")));
+ } else if (qName.equals("exception")) {
+ mCurrentMethod.addException(attributes.getValue("type"));
+ } else if (qName.equals("implements")) {
+ mCurrentClass.addInterface(attributes.getValue("name"));
+ }
+ }
+ public void endElement(String uri, String localName, String qName) {
+ if (qName.equals("method")) {
+ mCurrentClass.addMethod((MethodInfo) mCurrentMethod);
+ } else if (qName.equals("constructor")) {
+ mCurrentClass.addConstructor((ConstructorInfo) mCurrentMethod);
+ } else if (qName.equals("class")
+ || qName.equals("interface")) {
+ mCurrentPackage.addClass(mCurrentClass);
+ mCurrentClass = mClassScope.pop();
+ } else if (qName.equals("package")){
+ if (mOldApi) {
+ mOriginalApi.addPackage(mCurrentPackage);
+ } else {
+ mNewApi.addPackage(mCurrentPackage);
+ }
+ }
+ }
+ public void endDocument() {
+ mOldApi = !mOldApi;
+ }
+
+ public ApiInfo getOldApi() {
+ return mOriginalApi;
+ }
+
+ public ApiInfo getNewApi() {
+ return mNewApi;
+ }
+
+
+ }
+}
diff --git a/tools/apicheck/src/com/android/apicheck/ApiInfo.java b/tools/apicheck/src/com/android/apicheck/ApiInfo.java
new file mode 100644
index 000000000..01d8f9e76
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/ApiInfo.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+import java.util.*;
+
+public class ApiInfo {
+
+ private HashMap<String, PackageInfo> mPackages;
+ private HashMap<String, ClassInfo> mAllClasses;
+
+ public ApiInfo() {
+ mPackages = new HashMap<String, PackageInfo>();
+ mAllClasses = new HashMap<String, ClassInfo>();
+ }
+
+ public boolean isConsistent(ApiInfo otherApi) {
+ boolean consistent = true;
+ for (PackageInfo pInfo : mPackages.values()) {
+ if (otherApi.getPackages().containsKey(pInfo.name())) {
+ if (!pInfo.isConsistent(otherApi.getPackages().get(pInfo.name()))) {
+ consistent = false;
+ }
+ } else {
+ Errors.error(Errors.REMOVED_PACKAGE, pInfo.position(),
+ "Removed package " + pInfo.name());
+ consistent = false;
+ }
+ }
+ for (PackageInfo pInfo : otherApi.mPackages.values()) {
+ if (!pInfo.isInBoth()) {
+ Errors.error(Errors.ADDED_PACKAGE, pInfo.position(),
+ "Added package " + pInfo.name());
+ consistent = false;
+ }
+ }
+ return consistent;
+ }
+
+ public HashMap<String, PackageInfo> getPackages() {
+ return mPackages;
+ }
+
+ public void addPackage(PackageInfo pInfo) {
+ // track the set of organized packages in the API
+ mPackages.put(pInfo.name(), pInfo);
+
+ // accumulate a direct map of all the classes in the API
+ for (ClassInfo cl: pInfo.allClasses().values()) {
+ mAllClasses.put(cl.qualifiedName(), cl);
+ }
+ }
+
+ public void resolveSuperclasses() {
+ for (ClassInfo cl: mAllClasses.values()) {
+ // java.lang.Object has no superclass
+ if (!cl.qualifiedName().equals("java.lang.Object")) {
+ String scName = cl.superclassName();
+ if (scName == null) {
+ scName = "java.lang.Object";
+ }
+
+ ClassInfo superclass = mAllClasses.get(scName);
+ cl.setSuperClass(superclass);
+ }
+ }
+ }
+}
diff --git a/tools/apicheck/src/com/android/apicheck/ClassInfo.java b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
new file mode 100644
index 000000000..4bbf78b19
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/ClassInfo.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+import java.util.*;
+
+public class ClassInfo {
+ private String mName;
+ private String mSuperClassName;
+ private boolean mIsInterface;
+ private boolean mIsAbstract;
+ private boolean mIsStatic;
+ private boolean mIsFinal;
+ private String mDeprecated;
+ private String mScope;
+ private List<String> mInterfaces;
+ private HashMap<String, MethodInfo> mMethods;
+ private HashMap<String, FieldInfo> mFields;
+ private HashMap<String, ConstructorInfo> mConstructors;
+ private boolean mExistsInBoth;
+ private PackageInfo mPackage;
+ private SourcePositionInfo mSourcePosition;
+ private ClassInfo mSuperClass;
+ private ClassInfo mParentClass;
+
+ public ClassInfo(String name, PackageInfo pack, String superClass, boolean isInterface,
+ boolean isAbstract, boolean isStatic, boolean isFinal, String deprecated,
+ String visibility, SourcePositionInfo source, ClassInfo parent) {
+ mName = name;
+ mPackage = pack;
+ mSuperClassName = superClass;
+ mIsInterface = isInterface;
+ mIsAbstract = isAbstract;
+ mIsStatic = isStatic;
+ mIsFinal = isFinal;
+ mDeprecated = deprecated;
+ mScope = visibility;
+ mInterfaces = new ArrayList<String>();
+ mMethods = new HashMap<String, MethodInfo>();
+ mFields = new HashMap<String, FieldInfo>();
+ mConstructors = new HashMap<String, ConstructorInfo>();
+ mExistsInBoth = false;
+ mSourcePosition = source;
+ mParentClass = parent;
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ public String qualifiedName() {
+ String parentQName = (mParentClass != null)
+ ? (mParentClass.qualifiedName() + ".")
+ : "";
+ return mPackage.name() + "." + parentQName + name();
+ }
+
+ public String superclassName() {
+ return mSuperClassName;
+ }
+
+ public SourcePositionInfo position() {
+ return mSourcePosition;
+ }
+
+ public boolean isInterface() {
+ return mIsInterface;
+ }
+
+ public boolean isFinal() {
+ return mIsFinal;
+ }
+
+ // Find a superclass implementation of the given method. Looking at our superclass
+ // instead of at 'this' is unusual, but it fits the point-of-call demands well.
+ public MethodInfo overriddenMethod(MethodInfo candidate) {
+ if (mSuperClass == null) {
+ return null;
+ }
+
+ // does our immediate superclass have it?
+ ClassInfo sup = mSuperClass;
+ for (MethodInfo mi : sup.mMethods.values()) {
+ if (mi.matches(candidate)) {
+ // found it
+ return mi;
+ }
+ }
+
+ // no, so recurse
+ if (sup.mSuperClass != null) {
+ return mSuperClass.overriddenMethod(candidate);
+ }
+
+ // no parent, so we just don't have it
+ return null;
+ }
+
+ public boolean isConsistent(ClassInfo cl) {
+ cl.mExistsInBoth = true;
+ mExistsInBoth = true;
+ boolean consistent = true;
+
+ if (isInterface() != cl.isInterface()) {
+ Errors.error(Errors.CHANGED_CLASS, cl.position(),
+ "Class " + cl.qualifiedName()
+ + " changed class/interface declaration");
+ consistent = false;
+ }
+ for (String iface : mInterfaces) {
+ if (!cl.mInterfaces.contains(iface)) {
+ Errors.error(Errors.REMOVED_INTERFACE, cl.position(),
+ "Class " + qualifiedName() + " no longer implements " + iface);
+ }
+ }
+ for (String iface : cl.mInterfaces) {
+ if (!mInterfaces.contains(iface)) {
+ Errors.error(Errors.ADDED_INTERFACE, cl.position(),
+ "Added interface " + iface + " to class "
+ + qualifiedName());
+ consistent = false;
+ }
+ }
+
+ for (MethodInfo mInfo : mMethods.values()) {
+ if (cl.mMethods.containsKey(mInfo.getHashableName())) {
+ if (!mInfo.isConsistent(cl.mMethods.get(mInfo.getHashableName()))) {
+ consistent = false;
+ }
+ } else {
+ /* This class formerly provided this method directly, and now does not.
+ * Check our ancestry to see if there's an inherited version that still
+ * fulfills the API requirement.
+ */
+ MethodInfo mi = mInfo.containingClass().overriddenMethod(mInfo);
+ if (mi == null) {
+ Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
+ "Removed public method " + mInfo.qualifiedName());
+ consistent = false;
+ }
+ }
+ }
+ for (MethodInfo mInfo : cl.mMethods.values()) {
+ if (!mInfo.isInBoth()) {
+ /* Similarly to the above, do not fail if this "new" method is
+ * really an override of an existing superclass method.
+ */
+ MethodInfo mi = mInfo.containingClass().overriddenMethod(mInfo);
+ if (mi == null) {
+ Errors.error(Errors.ADDED_METHOD, mInfo.position(),
+ "Added public method " + mInfo.qualifiedName());
+ consistent = false;
+ }
+ }
+ }
+
+ for (ConstructorInfo mInfo : mConstructors.values()) {
+ if (cl.mConstructors.containsKey(mInfo.getHashableName())) {
+ if (!mInfo.isConsistent(cl.mConstructors.get(mInfo.getHashableName()))) {
+ consistent = false;
+ }
+ } else {
+ Errors.error(Errors.REMOVED_METHOD, mInfo.position(),
+ "Removed public constructor " + mInfo.prettySignature());
+ consistent = false;
+ }
+ }
+ for (ConstructorInfo mInfo : cl.mConstructors.values()) {
+ if (!mInfo.isInBoth()) {
+ Errors.error(Errors.ADDED_METHOD, mInfo.position(),
+ "Added public constructor " + mInfo.prettySignature());
+ consistent = false;
+ }
+ }
+
+ for (FieldInfo mInfo : mFields.values()) {
+ if (cl.mFields.containsKey(mInfo.qualifiedName())) {
+ if (!mInfo.isConsistent(cl.mFields.get(mInfo.qualifiedName()))) {
+ consistent = false;
+ }
+ } else {
+ Errors.error(Errors.REMOVED_FIELD, mInfo.position(),
+ "Removed field " + mInfo.qualifiedName());
+ consistent = false;
+ }
+ }
+ for (FieldInfo mInfo : cl.mFields.values()) {
+ if (!mInfo.isInBoth()) {
+ Errors.error(Errors.ADDED_FIELD, mInfo.position(),
+ "Added public field " + mInfo.qualifiedName());
+ consistent = false;
+ }
+ }
+
+ if (mIsAbstract != cl.mIsAbstract) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_ABSTRACT, cl.position(),
+ "Class " + cl.qualifiedName() + " changed abstract qualifier");
+ }
+
+ if (mIsFinal != cl.mIsFinal) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_FINAL, cl.position(),
+ "Class " + cl.qualifiedName() + " changed final qualifier");
+ }
+
+ if (mIsStatic != cl.mIsStatic) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_STATIC, cl.position(),
+ "Class " + cl.qualifiedName() + " changed static qualifier");
+ }
+
+ if (!mScope.equals(cl.mScope)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_SCOPE, cl.position(),
+ "Class " + cl.qualifiedName() + " scope changed from "
+ + mScope + " to " + cl.mScope);
+ }
+
+ if (!mDeprecated.equals(cl.mDeprecated)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_DEPRECATED, cl.position(),
+ "Class " + cl.qualifiedName() + " has changed deprecation state");
+ }
+
+ if (mSuperClassName != null) {
+ if (cl.mSuperClassName == null || !mSuperClassName.equals(cl.mSuperClassName)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(),
+ "Class " + qualifiedName() + " superclass changed from "
+ + mSuperClassName + " to " + cl.mSuperClassName);
+ }
+ } else if (cl.mSuperClassName != null) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_SUPERCLASS, cl.position(),
+ "Class " + qualifiedName() + " superclass changed from "
+ + "null to " + cl.mSuperClassName);
+ }
+
+ return consistent;
+ }
+
+ public void addInterface(String name) {
+ mInterfaces.add(name);
+ }
+
+ public void addMethod(MethodInfo mInfo) {
+ mMethods.put(mInfo.getHashableName(), mInfo);
+ }
+
+ public void addConstructor(ConstructorInfo cInfo) {
+ mConstructors.put(cInfo.getHashableName(), cInfo);
+
+ }
+
+ public void addField(FieldInfo fInfo) {
+ mFields.put(fInfo.qualifiedName(), fInfo);
+
+ }
+
+ public void setSuperClass(ClassInfo superclass) {
+ mSuperClass = superclass;
+ }
+
+ public boolean isInBoth() {
+ return mExistsInBoth;
+ }
+
+}
diff --git a/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
new file mode 100644
index 000000000..57d761716
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/ConstructorInfo.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+import java.util.*;
+
+public class ConstructorInfo implements AbstractMethodInfo {
+
+ private String mName;
+ private String mType;
+ private boolean mIsStatic;
+ private boolean mIsFinal;
+ private String mDeprecated;
+ private String mScope;
+ private List<String> mExceptions;
+ private List<ParameterInfo> mParameters;
+ private boolean mExistsInBoth;
+ private SourcePositionInfo mSourcePosition;
+ private ClassInfo mClass;
+
+ public ConstructorInfo(String name, String type, boolean isStatic, boolean isFinal,
+ String deprecated, String scope, SourcePositionInfo pos, ClassInfo clazz) {
+ mName = name;
+ mType = type;
+ mIsStatic = isStatic;
+ mIsFinal = isFinal;
+ mDeprecated= deprecated;
+ mScope = scope;
+ mExistsInBoth = false;
+ mExceptions = new ArrayList<String>();
+ mParameters = new ArrayList<ParameterInfo>();
+ mSourcePosition = pos;
+ mClass = clazz;
+ }
+
+ public void addParameter(ParameterInfo pInfo) {
+ mParameters.add(pInfo);
+ }
+
+ public void addException(String exec) {
+ mExceptions.add(exec);
+ }
+
+ public String getHashableName() {
+ String returnString = qualifiedName();
+ for (ParameterInfo pInfo : mParameters) {
+ returnString += ":" + pInfo.getType();
+ }
+ return returnString;
+ }
+
+ public boolean isInBoth() {
+ return mExistsInBoth;
+ }
+
+ public SourcePositionInfo position() {
+ return mSourcePosition;
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ public String qualifiedName() {
+ String baseName = (mClass != null)
+ ? (mClass.qualifiedName() + ".")
+ : "";
+ return baseName + name();
+ }
+
+ public String prettySignature() {
+ String params = "";
+ for (ParameterInfo pInfo : mParameters) {
+ if (params.length() > 0) {
+ params += ", ";
+ }
+ params += pInfo.getType();
+ }
+ return qualifiedName() + '(' + params + ')';
+ }
+
+ public boolean isConsistent(ConstructorInfo mInfo) {
+ mInfo.mExistsInBoth = true;
+ mExistsInBoth = true;
+ boolean consistent = true;
+
+ if (mIsFinal != mInfo.mIsFinal) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_FINAL, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " has changed 'final' qualifier");
+ }
+
+ if (mIsStatic != mInfo.mIsStatic) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_FINAL, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " has changed 'static' qualifier");
+ }
+
+ if (!mScope.equals(mInfo.mScope)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_SCOPE, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " changed scope from "
+ + mScope + " to " + mInfo.mScope);
+ }
+
+ if (!mDeprecated.equals(mInfo.mDeprecated)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " has changed deprecation state");
+ }
+
+ for (String exec : mExceptions) {
+ if (!mInfo.mExceptions.contains(exec)) {
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " no longer throws exception "
+ + exec);
+ consistent = false;
+ }
+ }
+
+ for (String exec : mInfo.mExceptions) {
+ if (!mExceptions.contains(exec)) {
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(),
+ "Constructor " + mInfo.qualifiedName() + " added thrown exception "
+ + exec);
+ consistent = false;
+ }
+ }
+
+ return consistent;
+ }
+
+
+}
diff --git a/tools/apicheck/src/com/android/apicheck/Errors.java b/tools/apicheck/src/com/android/apicheck/Errors.java
new file mode 100644
index 000000000..d7013e3c1
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/Errors.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+import java.lang.Comparable;
+import java.util.TreeSet;
+
+public class Errors
+{
+ public static boolean hadError = false;
+ private static boolean warningsAreErrors = false;
+ private static TreeSet<Message> allErrors = new TreeSet<Message>();
+
+ private static class Message implements Comparable {
+ SourcePositionInfo pos;
+ String msg;
+
+ Message(SourcePositionInfo p, String m) {
+ pos = p;
+ msg = m;
+ }
+
+ public int compareTo(Object o) {
+ Message that = (Message)o;
+ int r = this.pos.compareTo(that.pos);
+ if (r != 0) return r;
+ return this.msg.compareTo(that.msg);
+ }
+
+ public String toString() {
+ return this.pos.toString() + this.msg;
+ }
+ }
+
+ public static void error(Error error, SourcePositionInfo where, String text) {
+ if (error.level == HIDDEN) {
+ return;
+ }
+
+ String which = (!warningsAreErrors && error.level == WARNING) ? " warning " : " error ";
+ String message = which + error.code + ": " + text;
+
+ if (where == null) {
+ where = new SourcePositionInfo("unknown", 0, 0);
+ }
+
+ allErrors.add(new Message(where, message));
+
+ if (error.level == ERROR || (warningsAreErrors && error.level == WARNING)) {
+ hadError = true;
+ }
+ }
+
+ public static void printErrors() {
+ for (Message m: allErrors) {
+ System.err.println(m.toString());
+ }
+ }
+
+ public static int HIDDEN = 0;
+ public static int WARNING = 1;
+ public static int ERROR = 2;
+
+ public static void setWarningsAreErrors(boolean val) {
+ warningsAreErrors = val;
+ }
+
+ public static class Error {
+ public int code;
+ public int level;
+
+ public Error(int code, int level)
+ {
+ this.code = code;
+ this.level = level;
+ }
+ }
+
+ public static Error PARSE_ERROR = new Error(1, ERROR);
+ public static Error ADDED_PACKAGE = new Error(2, WARNING);
+ public static Error ADDED_CLASS = new Error(3, WARNING);
+ public static Error ADDED_METHOD = new Error(4, WARNING);
+ public static Error ADDED_FIELD = new Error(5, WARNING);
+ public static Error ADDED_INTERFACE = new Error(6, WARNING);
+ public static Error REMOVED_PACKAGE = new Error(7, WARNING);
+ public static Error REMOVED_CLASS = new Error(8, WARNING);
+ public static Error REMOVED_METHOD = new Error(9, WARNING);
+ public static Error REMOVED_FIELD = new Error(10, WARNING);
+ public static Error REMOVED_INTERFACE = new Error(11, WARNING);
+ public static Error CHANGED_STATIC = new Error(12, WARNING);
+ public static Error CHANGED_FINAL = new Error(13, WARNING);
+ public static Error CHANGED_TRANSIENT = new Error(14, WARNING);
+ public static Error CHANGED_VOLATILE = new Error(15, WARNING);
+ public static Error CHANGED_TYPE = new Error(16, WARNING);
+ public static Error CHANGED_VALUE = new Error(17, WARNING);
+ public static Error CHANGED_SUPERCLASS = new Error(18, WARNING);
+ public static Error CHANGED_SCOPE = new Error(19, WARNING);
+ public static Error CHANGED_ABSTRACT = new Error(20, WARNING);
+ public static Error CHANGED_THROWS = new Error(21, WARNING);
+ public static Error CHANGED_NATIVE = new Error(22, HIDDEN);
+ public static Error CHANGED_CLASS = new Error(23, WARNING);
+ public static Error CHANGED_DEPRECATED = new Error(24, WARNING);
+ public static Error CHANGED_SYNCHRONIZED = new Error(25, ERROR);
+
+ public static Error[] ERRORS = {
+ PARSE_ERROR,
+ ADDED_PACKAGE,
+ ADDED_CLASS,
+ ADDED_METHOD,
+ ADDED_FIELD,
+ ADDED_INTERFACE,
+ REMOVED_PACKAGE,
+ REMOVED_CLASS,
+ REMOVED_METHOD,
+ REMOVED_FIELD,
+ REMOVED_INTERFACE,
+ CHANGED_STATIC,
+ CHANGED_FINAL,
+ CHANGED_TRANSIENT,
+ CHANGED_VOLATILE,
+ CHANGED_TYPE,
+ CHANGED_VALUE,
+ CHANGED_SUPERCLASS,
+ CHANGED_SCOPE,
+ CHANGED_ABSTRACT,
+ CHANGED_THROWS,
+ CHANGED_NATIVE,
+ CHANGED_CLASS,
+ CHANGED_DEPRECATED,
+ CHANGED_SYNCHRONIZED,
+ };
+
+ public static boolean setErrorLevel(int code, int level) {
+ for (Error e: ERRORS) {
+ if (e.code == code) {
+ e.level = level;
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tools/apicheck/src/com/android/apicheck/FieldInfo.java b/tools/apicheck/src/com/android/apicheck/FieldInfo.java
new file mode 100644
index 000000000..d80d9f6a6
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/FieldInfo.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+public class FieldInfo {
+
+ private String mName;
+ private String mType;
+ private boolean mIsTransient;
+ private boolean mIsVolatile;
+ private String mValue;
+ private boolean mIsStatic;
+ private boolean mIsFinal;
+ private String mDeprecated;
+ private String mScope;
+ private boolean mExistsInBoth;
+ private SourcePositionInfo mSourcePosition;
+ private ClassInfo mClass;
+
+ public FieldInfo (String name, String type, boolean isTransient, boolean isVolatile,
+ String value, boolean isStatic, boolean isFinal, String deprecated,
+ String scope, SourcePositionInfo source, ClassInfo parent) {
+ mName = name;
+ mType = type;
+ mIsTransient = isTransient;
+ mIsVolatile = isVolatile;
+ mValue = value;
+ mIsStatic = isStatic;
+ mIsFinal = isFinal;
+ mDeprecated = deprecated;
+ mScope = scope;
+ mExistsInBoth = false;
+ mSourcePosition = source;
+ mClass = parent;
+ }
+
+ public boolean isInBoth() {
+ return mExistsInBoth;
+ }
+ public SourcePositionInfo position() {
+ return mSourcePosition;
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ public String qualifiedName() {
+ String parentQName = (mClass != null)
+ ? (mClass.qualifiedName() + ".")
+ : "";
+ return parentQName + name();
+ }
+
+ // Check the declared value with a typed comparison, not a string comparison,
+ // to accommodate toolchains with different fp -> string conversions.
+ public boolean valueEquals(FieldInfo other) {
+ // Type mismatch means nonequal, as does a null/non-null mismatch
+ if (!mType.equals(other.mType)
+ || ((mValue == null) != (other.mValue == null))) {
+ return false;
+ }
+
+ // Null values are considered equal
+ if (mValue == null) {
+ return true;
+ }
+
+ // Floating point gets an implementation-type comparison; all others just use the string
+ // If float/double parse fails, fall back to string comparison -- it means that it's a
+ // canonical droiddoc-generated constant expression that represents a NaN.
+ try {
+ if (mType.equals("float")) {
+ float val = Float.parseFloat(mValue);
+ float otherVal = Float.parseFloat(other.mValue);
+ return (val == otherVal);
+ } else if (mType.equals("double")) {
+ double val = Double.parseDouble(mValue);
+ double otherVal = Double.parseDouble(other.mValue);
+ return (val == otherVal);
+ }
+ } catch (NumberFormatException e) {
+ // fall through
+ }
+
+ return mValue.equals(other.mValue);
+ }
+
+ public boolean isConsistent(FieldInfo fInfo) {
+ fInfo.mExistsInBoth = true;
+ mExistsInBoth = true;
+ boolean consistent = true;
+ if (!mType.equals(fInfo.mType)) {
+ Errors.error(Errors.CHANGED_TYPE, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed type");
+ consistent = false;
+ }
+
+ if (!this.valueEquals(fInfo)) {
+ Errors.error(Errors.CHANGED_VALUE, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed value from "
+ + mValue + " to " + fInfo.mValue);
+ consistent = false;
+ }
+
+ if (!mScope.equals(fInfo.mScope)) {
+ Errors.error(Errors.CHANGED_SCOPE, fInfo.position(),
+ "Method " + fInfo.qualifiedName() + " changed scope from "
+ + mScope + " to " + fInfo.mScope);
+ consistent = false;
+ }
+
+ if (mIsStatic != fInfo.mIsStatic) {
+ Errors.error(Errors.CHANGED_STATIC, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed 'static' qualifier");
+ consistent = false;
+ }
+
+ if (mIsFinal != fInfo.mIsFinal) {
+ Errors.error(Errors.CHANGED_FINAL, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed 'final' qualifier");
+ consistent = false;
+ }
+
+ if (mIsTransient != fInfo.mIsTransient) {
+ Errors.error(Errors.CHANGED_TRANSIENT, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed 'transient' qualifier");
+ consistent = false;
+ }
+
+ if (mIsVolatile != fInfo.mIsVolatile) {
+ Errors.error(Errors.CHANGED_VOLATILE, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed 'volatile' qualifier");
+ consistent = false;
+ }
+
+ if (!mDeprecated.equals(fInfo.mDeprecated)) {
+ Errors.error(Errors.CHANGED_DEPRECATED, fInfo.position(),
+ "Field " + fInfo.qualifiedName() + " has changed deprecation state");
+ consistent = false;
+ }
+
+ return consistent;
+ }
+
+}
diff --git a/tools/apicheck/src/com/android/apicheck/MethodInfo.java b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
new file mode 100644
index 000000000..86e20deed
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/MethodInfo.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+import java.util.*;
+
+public class MethodInfo implements AbstractMethodInfo {
+
+ private String mName;
+ private String mReturn;
+ private boolean mIsAbstract;
+ private boolean mIsNative;
+ private boolean mIsSynchronized;
+ private boolean mIsStatic;
+ private boolean mIsFinal;
+ private String mDeprecated;
+ private String mScope;
+ private boolean mExistsInBoth;
+ private List<ParameterInfo> mParameters;
+ private List<String> mExceptions;
+ private SourcePositionInfo mSourcePosition;
+ private ClassInfo mClass;
+
+ public MethodInfo (String name, String returnType, boolean isAbstract, boolean isNative,
+ boolean isSynchronized, boolean isStatic, boolean isFinal, String deprecated
+ , String scope, SourcePositionInfo source, ClassInfo parent) {
+
+ mName = name;
+ mReturn = returnType;
+ mIsAbstract = isAbstract;
+ mIsNative = isNative;
+ mIsSynchronized = isSynchronized;
+ mIsStatic = isStatic;
+ mIsFinal = isFinal;
+ mDeprecated = deprecated;
+ mScope = scope;
+ mParameters = new ArrayList<ParameterInfo>();
+ mExceptions = new ArrayList<String>();
+ mExistsInBoth = false;
+ mSourcePosition = source;
+ mClass = parent;
+ }
+
+
+ public String name() {
+ return mName;
+ }
+
+ public String qualifiedName() {
+ String parentQName = (mClass != null)
+ ? (mClass.qualifiedName() + ".")
+ : "";
+ return parentQName + name();
+ }
+
+ public String prettySignature() {
+ String params = "";
+ for (ParameterInfo pInfo : mParameters) {
+ if (params.length() > 0) {
+ params += ", ";
+ }
+ params += pInfo.getType();
+ }
+ return qualifiedName() + '(' + params + ')';
+ }
+
+ public SourcePositionInfo position() {
+ return mSourcePosition;
+ }
+
+ public ClassInfo containingClass() {
+ return mClass;
+ }
+
+ public boolean matches(MethodInfo other) {
+ return getSignature().equals(other.getSignature());
+ }
+
+ public boolean isConsistent(MethodInfo mInfo) {
+ mInfo.mExistsInBoth = true;
+ mExistsInBoth = true;
+ boolean consistent = true;
+ if (!mReturn.equals(mInfo.mReturn)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_TYPE, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed return type from "
+ + mReturn + " to " + mInfo.mReturn);
+ }
+
+ if (mIsAbstract != mInfo.mIsAbstract) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_ABSTRACT, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed 'abstract' qualifier");
+ }
+
+ if (mIsNative != mInfo.mIsNative) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_NATIVE, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed 'native' qualifier");
+ }
+
+ if (mIsFinal != mInfo.mIsFinal) {
+ // Compiler-generated methods vary in their 'final' qual between versions of
+ // the compiler, so this check needs to be quite narrow. A change in 'final'
+ // status of a method is only relevant if (a) the method is not declared 'static'
+ // and (b) the method's class is not itself 'final'.
+ if (!mIsStatic) {
+ if ((mClass == null) || (!mClass.isFinal())) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_FINAL, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed 'final' qualifier");
+ }
+ }
+ }
+
+ if (mIsStatic != mInfo.mIsStatic) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_STATIC, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed 'static' qualifier");
+ }
+
+ if (!mScope.equals(mInfo.mScope)) {
+ consistent = false;
+ Errors.error(Errors.CHANGED_SCOPE, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " changed scope from "
+ + mScope + " to " + mInfo.mScope);
+ }
+
+ if (!mDeprecated.equals(mInfo.mDeprecated)) {
+ Errors.error(Errors.CHANGED_DEPRECATED, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed deprecation state");
+ consistent = false;
+ }
+
+ if (mIsSynchronized != mInfo.mIsSynchronized) {
+ Errors.error(Errors.CHANGED_SYNCHRONIZED, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " has changed 'synchronized' qualifier from " + mIsSynchronized + " to " + mInfo.mIsSynchronized);
+ consistent = false;
+ }
+
+ for (String exec : mExceptions) {
+ if (!mInfo.mExceptions.contains(exec)) {
+ // exclude 'throws' changes to finalize() overrides with no arguments
+ if (!name().equals("finalize") || (mParameters.size() > 0)) {
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " no longer throws exception "
+ + exec);
+ consistent = false;
+ }
+ }
+ }
+
+ for (String exec : mInfo.mExceptions) {
+ // exclude 'throws' changes to finalize() overrides with no arguments
+ if (!mExceptions.contains(exec)) {
+ if (!name().equals("finalize") || (mParameters.size() > 0)) {
+ Errors.error(Errors.CHANGED_THROWS, mInfo.position(),
+ "Method " + mInfo.qualifiedName() + " added thrown exception "
+ + exec);
+ consistent = false;
+ }
+ }
+ }
+
+ return consistent;
+ }
+
+ public void addParameter(ParameterInfo pInfo) {
+ mParameters.add(pInfo);
+ }
+
+ public void addException(String exc) {
+ mExceptions.add(exc);
+ }
+
+ public String getParameterHash() {
+ String hash = "";
+ for (ParameterInfo pInfo : mParameters) {
+ hash += ":" + pInfo.getType();
+ }
+ return hash;
+ }
+
+ public String getHashableName() {
+ return qualifiedName() + getParameterHash();
+ }
+
+ public String getSignature() {
+ return name() + getParameterHash();
+ }
+
+ public boolean isInBoth() {
+ return mExistsInBoth;
+ }
+
+}
diff --git a/tools/apicheck/src/com/android/apicheck/PackageInfo.java b/tools/apicheck/src/com/android/apicheck/PackageInfo.java
new file mode 100644
index 000000000..2262f21fd
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/PackageInfo.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+import java.util.*;
+
+public class PackageInfo {
+ private String mName;
+ private HashMap<String, ClassInfo> mClasses;
+ private boolean mExistsInBoth;
+ private SourcePositionInfo mPosition;
+
+ public PackageInfo(String name, SourcePositionInfo position) {
+ mName = name;
+ mClasses = new HashMap<String, ClassInfo>();
+ mExistsInBoth = false;
+ mPosition = position;
+ }
+
+ public void addClass(ClassInfo cl) {
+ mClasses.put(cl.name() , cl);
+ }
+
+ public HashMap<String, ClassInfo> allClasses() {
+ return mClasses;
+ }
+
+ public String name() {
+ return mName;
+ }
+
+ public SourcePositionInfo position() {
+ return mPosition;
+ }
+
+ public boolean isConsistent(PackageInfo pInfo) {
+ mExistsInBoth = true;
+ pInfo.mExistsInBoth = true;
+ boolean consistent = true;
+ for (ClassInfo cInfo : mClasses.values()) {
+ if (pInfo.mClasses.containsKey(cInfo.name())) {
+ if (!cInfo.isConsistent(pInfo.mClasses.get(cInfo.name()))) {
+ consistent = false;
+ }
+ } else {
+ Errors.error(Errors.REMOVED_CLASS, cInfo.position(),
+ "Removed public class " + cInfo.qualifiedName());
+ consistent = false;
+ }
+ }
+ for (ClassInfo cInfo : pInfo.mClasses.values()) {
+ if (!cInfo.isInBoth()) {
+ Errors.error(Errors.ADDED_CLASS, cInfo.position(),
+ "Added class " + cInfo.name() + " to package "
+ + pInfo.name());
+ consistent = false;
+ }
+ }
+ return consistent;
+ }
+
+ public boolean isInBoth() {
+ return mExistsInBoth;
+ }
+}
diff --git a/tools/apicheck/src/com/android/apicheck/ParameterInfo.java b/tools/apicheck/src/com/android/apicheck/ParameterInfo.java
new file mode 100644
index 000000000..5788814cb
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/ParameterInfo.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+public class ParameterInfo {
+ private String mType;
+ private String mName;
+
+ public ParameterInfo(String type, String name) {
+ mType = type;
+ mName = name;
+ }
+
+ public String getType() {
+ return mType;
+ }
+
+ public String getName() {
+ return mName;
+ }
+}
diff --git a/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java
new file mode 100644
index 000000000..477c1d356
--- /dev/null
+++ b/tools/apicheck/src/com/android/apicheck/SourcePositionInfo.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.apicheck;
+
+import java.lang.Comparable;
+
+public class SourcePositionInfo implements Comparable
+{
+ public SourcePositionInfo() {
+ this.file = "<unknown>";
+ this.line = 0;
+ this.column = 0;
+ }
+
+ public SourcePositionInfo(String file, int line, int column)
+ {
+ this.file = file;
+ this.line = line;
+ this.column = column;
+ }
+
+ public SourcePositionInfo(SourcePositionInfo that)
+ {
+ this.file = that.file;
+ this.line = that.line;
+ this.column = that.column;
+ }
+
+ /**
+ * Given this position and str which occurs at that position, as well as str an index into str,
+ * find the SourcePositionInfo.
+ *
+ * @throw StringIndexOutOfBoundsException if index &gt; str.length()
+ */
+ public static SourcePositionInfo add(SourcePositionInfo that, String str, int index)
+ {
+ if (that == null) {
+ return null;
+ }
+ int line = that.line;
+ char prev = 0;
+ for (int i=0; i<index; i++) {
+ char c = str.charAt(i);
+ if (c == '\r' || (c == '\n' && prev != '\r')) {
+ line++;
+ }
+ prev = c;
+ }
+ return new SourcePositionInfo(that.file, line, 0);
+ }
+
+ public static SourcePositionInfo findBeginning(SourcePositionInfo that, String str)
+ {
+ if (that == null) {
+ return null;
+ }
+ int line = that.line-1; // -1 because, well, it seems to work
+ int prev = 0;
+ for (int i=str.length()-1; i>=0; i--) {
+ char c = str.charAt(i);
+ if ((c == '\r' && prev != '\n') || (c == '\n')) {
+ line--;
+ }
+ prev = c;
+ }
+ return new SourcePositionInfo(that.file, line, 0);
+ }
+
+ public String toString()
+ {
+ if (this.file == null) {
+ return "(unknown)";
+ } else {
+ if (this.line == 0) {
+ return this.file + ':';
+ } else {
+ return this.file + ':' + this.line + ':';
+ }
+ }
+ }
+
+ public int compareTo(Object o) {
+ SourcePositionInfo that = (SourcePositionInfo)o;
+ int r = this.file.compareTo(that.file);
+ if (r != 0) return r;
+ return this.line - that.line;
+ }
+
+ /**
+ * Build a SourcePositionInfo from the XML source= notation
+ */
+ public static SourcePositionInfo fromXml(String source) {
+ if (source != null) {
+ for (int i = 0; i < source.length(); i++) {
+ if (source.charAt(i) == ':') {
+ return new SourcePositionInfo(source.substring(0, i),
+ Integer.parseInt(source.substring(i+1)), 0);
+ }
+ }
+ }
+
+ return new SourcePositionInfo("(unknown)", 0, 0);
+ }
+
+ public String file;
+ public int line;
+ public int column;
+}
diff --git a/tools/applypatch/Android.mk b/tools/applypatch/Android.mk
new file mode 100644
index 000000000..09f986245
--- /dev/null
+++ b/tools/applypatch/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_SRC_FILES := applypatch.c bsdiff.c freecache.c
+LOCAL_MODULE := applypatch
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES += external/bzip2
+LOCAL_STATIC_LIBRARIES += libmincrypt libbz libc
+
+include $(BUILD_EXECUTABLE)
+
+endif # !TARGET_SIMULATOR
diff --git a/tools/applypatch/applypatch.c b/tools/applypatch/applypatch.c
new file mode 100644
index 000000000..9954869c7
--- /dev/null
+++ b/tools/applypatch/applypatch.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+
+#include "mincrypt/sha.h"
+#include "applypatch.h"
+
+// Read a file into memory; store it and its associated metadata in
+// *file. Return 0 on success.
+int LoadFileContents(const char* filename, FileContents* file) {
+ file->data = NULL;
+
+ if (stat(filename, &file->st) != 0) {
+ fprintf(stderr, "failed to stat \"%s\": %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ file->size = file->st.st_size;
+ file->data = malloc(file->size);
+
+ FILE* f = fopen(filename, "rb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open \"%s\": %s\n", filename, strerror(errno));
+ free(file->data);
+ return -1;
+ }
+
+ size_t bytes_read = fread(file->data, 1, file->size, f);
+ if (bytes_read != file->size) {
+ fprintf(stderr, "short read of \"%s\" (%d bytes of %d)\n",
+ filename, bytes_read, file->size);
+ free(file->data);
+ return -1;
+ }
+ fclose(f);
+
+ SHA(file->data, file->size, file->sha1);
+ return 0;
+}
+
+// Save the contents of the given FileContents object under the given
+// filename. Return 0 on success.
+int SaveFileContents(const char* filename, FileContents file) {
+ FILE* f = fopen(filename, "wb");
+ if (f == NULL) {
+ fprintf(stderr, "failed to open \"%s\" for write: %s\n",
+ filename, strerror(errno));
+ return -1;
+ }
+
+ size_t bytes_written = fwrite(file.data, 1, file.size, f);
+ if (bytes_written != file.size) {
+ fprintf(stderr, "short write of \"%s\" (%d bytes of %d)\n",
+ filename, bytes_written, file.size);
+ return -1;
+ }
+ fflush(f);
+ fsync(fileno(f));
+ fclose(f);
+
+ if (chmod(filename, file.st.st_mode) != 0) {
+ fprintf(stderr, "chmod of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ if (chown(filename, file.st.st_uid, file.st.st_gid) != 0) {
+ fprintf(stderr, "chown of \"%s\" failed: %s\n", filename, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+// Take a string 'str' of 40 hex digits and parse it into the 20
+// byte array 'digest'. 'str' may contain only the digest or be of
+// the form "<digest>:<anything>". Return 0 on success, -1 on any
+// error.
+int ParseSha1(const char* str, uint8_t* digest) {
+ int i;
+ const char* ps = str;
+ uint8_t* pd = digest;
+ for (i = 0; i < SHA_DIGEST_SIZE * 2; ++i, ++ps) {
+ int digit;
+ if (*ps >= '0' && *ps <= '9') {
+ digit = *ps - '0';
+ } else if (*ps >= 'a' && *ps <= 'f') {
+ digit = *ps - 'a' + 10;
+ } else if (*ps >= 'A' && *ps <= 'F') {
+ digit = *ps - 'A' + 10;
+ } else {
+ return -1;
+ }
+ if (i % 2 == 0) {
+ *pd = digit << 4;
+ } else {
+ *pd |= digit;
+ ++pd;
+ }
+ }
+ if (*ps != '\0' && *ps != ':') return -1;
+ return 0;
+}
+
+// Parse arguments (which should be of the form "<sha1>" or
+// "<sha1>:<filename>" into the array *patches, returning the number
+// of Patch objects in *num_patches. Return 0 on success.
+int ParseShaArgs(int argc, char** argv, Patch** patches, int* num_patches) {
+ *num_patches = argc;
+ *patches = malloc(*num_patches * sizeof(Patch));
+
+ int i;
+ for (i = 0; i < *num_patches; ++i) {
+ if (ParseSha1(argv[i], (*patches)[i].sha1) != 0) {
+ fprintf(stderr, "failed to parse sha1 \"%s\"\n", argv[i]);
+ return -1;
+ }
+ if (argv[i][SHA_DIGEST_SIZE*2] == '\0') {
+ (*patches)[i].patch_filename = NULL;
+ } else if (argv[i][SHA_DIGEST_SIZE*2] == ':') {
+ (*patches)[i].patch_filename = argv[i] + (SHA_DIGEST_SIZE*2+1);
+ } else {
+ fprintf(stderr, "failed to parse filename \"%s\"\n", argv[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+// Search an array of Patch objects for one matching the given sha1.
+// Return the Patch object on success, or NULL if no match is found.
+const Patch* FindMatchingPatch(uint8_t* sha1, Patch* patches, int num_patches) {
+ int i;
+ for (i = 0; i < num_patches; ++i) {
+ if (memcmp(patches[i].sha1, sha1, SHA_DIGEST_SIZE) == 0) {
+ return patches+i;
+ }
+ }
+ return NULL;
+}
+
+// Returns 0 if the contents of the file (argv[2]) or the cached file
+// match any of the sha1's on the command line (argv[3:]). Returns
+// nonzero otherwise.
+int CheckMode(int argc, char** argv) {
+ if (argc < 3) {
+ fprintf(stderr, "no filename given\n");
+ return 2;
+ }
+
+ int num_patches;
+ Patch* patches;
+ if (ParseShaArgs(argc-3, argv+3, &patches, &num_patches) != 0) { return 1; }
+
+ FileContents file;
+ file.data = NULL;
+
+ if (LoadFileContents(argv[2], &file) != 0 ||
+ FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
+ fprintf(stderr, "file \"%s\" doesn't have any of expected "
+ "sha1 sums; checking cache\n", argv[2]);
+
+ free(file.data);
+
+ // If the source file is missing or corrupted, it might be because
+ // we were killed in the middle of patching it. A copy of it
+ // should have been made in CACHE_TEMP_SOURCE. If that file
+ // exists and matches the sha1 we're looking for, the check still
+ // passes.
+
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &file) != 0) {
+ fprintf(stderr, "failed to load cache file\n");
+ return 1;
+ }
+
+ if (FindMatchingPatch(file.sha1, patches, num_patches) == NULL) {
+ fprintf(stderr, "cache bits don't match any sha1 for \"%s\"\n",
+ argv[2]);
+ return 1;
+ }
+ }
+
+ free(file.data);
+ return 0;
+}
+
+int ShowLicenses() {
+ ShowBSDiffLicense();
+ return 0;
+}
+
+// Return the amount of free space (in bytes) on the filesystem
+// containing filename. filename must exist. Return -1 on error.
+size_t FreeSpaceForFile(const char* filename) {
+ struct statfs sf;
+ if (statfs(filename, &sf) != 0) {
+ fprintf(stderr, "failed to statfs %s: %s\n", filename, strerror(errno));
+ return -1;
+ }
+ return sf.f_bsize * sf.f_bfree;
+}
+
+// This program applies binary patches to files in a way that is safe
+// (the original file is not touched until we have the desired
+// replacement for it) and idempotent (it's okay to run this program
+// multiple times).
+//
+// - if the sha1 hash of <file> is <tgt-sha1>, does nothing and exits
+// successfully.
+//
+// - otherwise, if the sha1 hash of <file> is <src-sha1>, applies the
+// bsdiff <patch> to <file> to produce a new file (the type of patch
+// is automatically detected from the file header). If that new
+// file has sha1 hash <tgt-sha1>, moves it to replace <file>, and
+// exits successfully.
+//
+// - otherwise, or if any error is encountered, exits with non-zero
+// status.
+
+int main(int argc, char** argv) {
+ if (argc < 2) {
+ usage:
+ fprintf(stderr, "usage: %s <file> <tgt-sha1> <tgt-size> [<src-sha1>:<patch> ...]\n"
+ " or %s -c <file> [<sha1> ...]\n"
+ " or %s -s <bytes>\n"
+ " or %s -l\n",
+ argv[0], argv[0], argv[0], argv[0]);
+ return 1;
+ }
+
+ if (strncmp(argv[1], "-l", 3) == 0) {
+ return ShowLicenses();
+ }
+
+ if (strncmp(argv[1], "-c", 3) == 0) {
+ return CheckMode(argc, argv);
+ }
+
+ if (strncmp(argv[1], "-s", 3) == 0) {
+ if (argc != 3) {
+ goto usage;
+ }
+ size_t bytes = strtol(argv[2], NULL, 10);
+ if (MakeFreeSpaceOnCache(bytes) < 0) {
+ printf("unable to make %ld bytes available on /cache\n", (long)bytes);
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+
+ uint8_t target_sha1[SHA_DIGEST_SIZE];
+
+ const char* source_filename = argv[1];
+
+ // assume that source_filename (eg "/system/app/Foo.apk") is located
+ // on the same filesystem as its top-level directory ("/system").
+ // We need something that exists for calling statfs().
+ char* source_fs = strdup(argv[1]);
+ char* slash = strchr(source_fs+1, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ }
+
+ if (ParseSha1(argv[2], target_sha1) != 0) {
+ fprintf(stderr, "failed to parse tgt-sha1 \"%s\"\n", argv[2]);
+ return 1;
+ }
+
+ unsigned long target_size = strtoul(argv[3], NULL, 0);
+
+ int num_patches;
+ Patch* patches;
+ if (ParseShaArgs(argc-4, argv+4, &patches, &num_patches) < 0) { return 1; }
+
+ FileContents copy_file;
+ FileContents source_file;
+ const char* source_patch_filename = NULL;
+ const char* copy_patch_filename = NULL;
+ int made_copy = 0;
+
+ if (LoadFileContents(source_filename, &source_file) == 0) {
+ if (memcmp(source_file.sha1, target_sha1, SHA_DIGEST_SIZE) == 0) {
+ // The early-exit case: the patch was already applied, this file
+ // has the desired hash, nothing for us to do.
+ fprintf(stderr, "\"%s\" is already target; no patch needed\n",
+ source_filename);
+ return 0;
+ }
+
+ const Patch* to_use =
+ FindMatchingPatch(source_file.sha1, patches, num_patches);
+ if (to_use != NULL) {
+ source_patch_filename = to_use->patch_filename;
+ }
+ }
+
+ if (source_patch_filename == NULL) {
+ free(source_file.data);
+ fprintf(stderr, "source file is bad; trying copy\n");
+
+ if (LoadFileContents(CACHE_TEMP_SOURCE, &copy_file) < 0) {
+ // fail.
+ fprintf(stderr, "failed to read copy file\n");
+ return 1;
+ }
+
+ const Patch* to_use =
+ FindMatchingPatch(copy_file.sha1, patches, num_patches);
+ if (to_use != NULL) {
+ copy_patch_filename = to_use->patch_filename;
+ }
+
+ if (copy_patch_filename == NULL) {
+ // fail.
+ fprintf(stderr, "copy file doesn't match source SHA-1s either\n");
+ return 1;
+ }
+ }
+
+ // Is there enough room in the target filesystem to hold the patched file?
+ size_t free_space = FreeSpaceForFile(source_fs);
+ int enough_space = free_space > (target_size * 3 / 2); // 50% margin of error
+ printf("target %ld bytes; free space %ld bytes; enough %d\n",
+ (long)target_size, (long)free_space, enough_space);
+
+ if (!enough_space && source_patch_filename != NULL) {
+ // Using the original source, but not enough free space. First
+ // copy the source file to cache, then delete it from the original
+ // location.
+ if (MakeFreeSpaceOnCache(source_file.size) < 0) {
+ fprintf(stderr, "not enough free space on /cache\n");
+ return 1;
+ }
+
+ if (SaveFileContents(CACHE_TEMP_SOURCE, source_file) < 0) {
+ fprintf(stderr, "failed to back up source file\n");
+ return 1;
+ }
+ made_copy = 1;
+ unlink(source_filename);
+
+ size_t free_space = FreeSpaceForFile(source_fs);
+ printf("(now %ld bytes free for source)\n", (long)free_space);
+ }
+
+ FileContents* source_to_use;
+ const char* patch_filename;
+ if (source_patch_filename != NULL) {
+ source_to_use = &source_file;
+ patch_filename = source_patch_filename;
+ } else {
+ source_to_use = &copy_file;
+ patch_filename = copy_patch_filename;
+ }
+
+ // We write the decoded output to "<file>.patch".
+ char* outname = (char*)malloc(strlen(source_filename) + 10);
+ strcpy(outname, source_filename);
+ strcat(outname, ".patch");
+ FILE* output = fopen(outname, "wb");
+ if (output == NULL) {
+ fprintf(stderr, "failed to patch file %s: %s\n",
+ source_filename, strerror(errno));
+ return 1;
+ }
+
+#define MAX_HEADER_LENGTH 8
+ unsigned char header[MAX_HEADER_LENGTH];
+ FILE* patchf = fopen(patch_filename, "rb");
+ if (patchf == NULL) {
+ fprintf(stderr, "failed to open patch file %s: %s\n",
+ patch_filename, strerror(errno));
+ return 1;
+ }
+ int header_bytes_read = fread(header, 1, MAX_HEADER_LENGTH, patchf);
+ fclose(patchf);
+
+ SHA_CTX ctx;
+ SHA_init(&ctx);
+
+ if (header_bytes_read >= 4 &&
+ header[0] == 0xd6 && header[1] == 0xc3 &&
+ header[2] == 0xc4 && header[3] == 0) {
+ // xdelta3 patches begin "VCD" (with the high bits set) followed
+ // by a zero byte (the version number).
+ fprintf(stderr, "error: xdelta3 patches no longer supported\n");
+ return 1;
+ } else if (header_bytes_read >= 8 &&
+ memcmp(header, "BSDIFF40", 8) == 0) {
+ int result = ApplyBSDiffPatch(source_to_use->data, source_to_use->size,
+ patch_filename, output, &ctx);
+ if (result != 0) {
+ fprintf(stderr, "ApplyBSDiffPatch failed\n");
+ return result;
+ }
+ } else {
+ fprintf(stderr, "Unknown patch file format");
+ return 1;
+ }
+
+ fflush(output);
+ fsync(fileno(output));
+ fclose(output);
+
+ const uint8_t* current_target_sha1 = SHA_final(&ctx);
+ if (memcmp(current_target_sha1, target_sha1, SHA_DIGEST_SIZE) != 0) {
+ fprintf(stderr, "patch did not produce expected sha1\n");
+ return 1;
+ }
+
+ // Give the .patch file the same owner, group, and mode of the
+ // original source file.
+ if (chmod(outname, source_to_use->st.st_mode) != 0) {
+ fprintf(stderr, "chmod of \"%s\" failed: %s\n", outname, strerror(errno));
+ return 1;
+ }
+ if (chown(outname, source_to_use->st.st_uid, source_to_use->st.st_gid) != 0) {
+ fprintf(stderr, "chown of \"%s\" failed: %s\n", outname, strerror(errno));
+ return 1;
+ }
+
+ // Finally, rename the .patch file to replace the original source file.
+ if (rename(outname, source_filename) != 0) {
+ fprintf(stderr, "rename of .patch to \"%s\" failed: %s\n",
+ source_filename, strerror(errno));
+ return 1;
+ }
+
+ // If this run of applypatch created the copy, and we're here, we
+ // can delete it.
+ if (made_copy) unlink(CACHE_TEMP_SOURCE);
+
+ // Success!
+ return 0;
+}
diff --git a/tools/applypatch/applypatch.h b/tools/applypatch/applypatch.h
new file mode 100644
index 000000000..76fc80aa9
--- /dev/null
+++ b/tools/applypatch/applypatch.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _APPLYPATCH_H
+#define _APPLYPATCH_H
+
+#include "mincrypt/sha.h"
+
+typedef struct _Patch {
+ uint8_t sha1[SHA_DIGEST_SIZE];
+ const char* patch_filename;
+} Patch;
+
+typedef struct _FileContents {
+ uint8_t sha1[SHA_DIGEST_SIZE];
+ unsigned char* data;
+ size_t size;
+ struct stat st;
+} FileContents;
+
+// When there isn't enough room on the target filesystem to hold the
+// patched version of the file, we copy the original here and delete
+// it to free up space. If the expected source file doesn't exist, or
+// is corrupted, we look to see if this file contains the bits we want
+// and use it as the source instead.
+#define CACHE_TEMP_SOURCE "/cache/saved.file"
+
+// applypatch.c
+size_t FreeSpaceForFile(const char* filename);
+
+// bsdiff.c
+void ShowBSDiffLicense();
+int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
+ const char* patch_filename,
+ FILE* output, SHA_CTX* ctx);
+
+// freecache.c
+int MakeFreeSpaceOnCache(size_t bytes_needed);
+
+#endif
diff --git a/tools/applypatch/applypatch.sh b/tools/applypatch/applypatch.sh
new file mode 100755
index 000000000..181cd5c48
--- /dev/null
+++ b/tools/applypatch/applypatch.sh
@@ -0,0 +1,272 @@
+#!/bin/bash
+#
+# A test suite for applypatch. Run in a client where you have done
+# envsetup, choosecombo, etc.
+#
+# DO NOT RUN THIS ON A DEVICE YOU CARE ABOUT. It will mess up your
+# system partition.
+#
+#
+# TODO: find some way to get this run regularly along with the rest of
+# the tests.
+
+EMULATOR_PORT=5580
+DATA_DIR=$ANDROID_BUILD_TOP/build/tools/applypatch/testdata
+
+# This must be the filename that applypatch uses for its copies.
+CACHE_TEMP_SOURCE=/cache/saved.file
+
+# Put all binaries and files here. We use /cache because it's a
+# temporary filesystem in the emulator; it's created fresh each time
+# the emulator starts.
+WORK_DIR=/system
+
+# partition that WORK_DIR is located on, without the leading slash
+WORK_FS=system
+
+# ------------------------
+
+tmpdir=$(mktemp -d)
+
+emulator -wipe-data -noaudio -no-window -port $EMULATOR_PORT &
+pid_emulator=$!
+
+ADB="adb -s emulator-$EMULATOR_PORT "
+
+echo "emulator is $pid_emulator; waiting for startup"
+$ADB wait-for-device
+echo "device is available"
+$ADB remount
+# free up enough space on the system partition for the test to run.
+$ADB shell rm -r /system/media
+
+# run a command on the device; exit with the exit status of the device
+# command.
+run_command() {
+ $ADB shell "$@" \; echo \$? | awk '{if (b) {print a}; a=$0; b=1} END {exit a}'
+}
+
+testname() {
+ echo
+ echo "$1"...
+ testname="$1"
+}
+
+fail() {
+ echo
+ echo FAIL: $testname
+ echo
+ kill $pid_emulator
+ exit 1
+}
+
+sha1() {
+ sha1sum $1 | awk '{print $1}'
+}
+
+free_space() {
+ run_command df | awk "/$1/ {print gensub(/K/, \"\", \"g\", \$6)}"
+}
+
+
+$ADB push $ANDROID_PRODUCT_OUT/system/bin/applypatch $WORK_DIR/applypatch
+
+BAD1_SHA1=$(printf "%040x" $RANDOM)
+BAD2_SHA1=$(printf "%040x" $RANDOM)
+OLD_SHA1=$(sha1 $DATA_DIR/old.file)
+NEW_SHA1=$(sha1 $DATA_DIR/new.file)
+NEW_SIZE=$(stat -c %s $DATA_DIR/new.file)
+
+# --------------- basic execution ----------------------
+
+testname "usage message"
+run_command $WORK_DIR/applypatch && fail
+
+testname "display license"
+run_command $WORK_DIR/applypatch -l | grep -q -i copyright || fail
+
+
+# --------------- check mode ----------------------
+
+$ADB push $DATA_DIR/old.file $WORK_DIR
+
+testname "check mode single"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
+
+testname "check mode multiple"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
+
+testname "check mode failure"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
+
+$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
+# put some junk in the old file
+run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
+
+testname "check mode cache (corrupted) single"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
+
+testname "check mode cache (corrupted) multiple"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
+
+testname "check mode cache (corrupted) failure"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
+
+# remove the old file entirely
+run_command rm $WORK_DIR/old.file
+
+testname "check mode cache (missing) single"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $OLD_SHA1 || fail
+
+testname "check mode cache (missing) multiple"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD1_SHA1 $OLD_SHA1 $BAD2_SHA1|| fail
+
+testname "check mode cache (missing) failure"
+run_command $WORK_DIR/applypatch -c $WORK_DIR/old.file $BAD2_SHA1 $BAD1_SHA1 && fail
+
+
+# --------------- apply patch ----------------------
+
+$ADB push $DATA_DIR/old.file $WORK_DIR
+$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
+
+# Check that the partition has enough space to apply the patch without
+# copying. If it doesn't, we'll be testing the low-space condition
+# when we intend to test the not-low-space condition.
+testname "apply patches (with enough space)"
+free_kb=$(free_space $WORK_FS)
+echo "${free_kb}kb free on /$WORK_FS."
+if (( free_kb * 1024 < NEW_SIZE * 3 / 2 )); then
+ echo "Not enough space on /$WORK_FS to patch test file."
+ echo
+ echo "This doesn't mean that applypatch is necessarily broken;"
+ echo "just that /$WORK_FS doesn't have enough free space to"
+ echo "properly run this test."
+ exit 1
+fi
+
+testname "apply bsdiff patch"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+testname "reapply bsdiff patch"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+
+# --------------- apply patch with low space on /system ----------------------
+
+$ADB push $DATA_DIR/old.file $WORK_DIR
+$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
+
+free_kb=$(free_space $WORK_FS)
+echo "${free_kb}kb free on /$WORK_FS; we'll soon fix that."
+echo run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
+run_command dd if=/dev/zero of=$WORK_DIR/bloat.dat count=$((free_kb-512)) bs=1024 || fail
+free_kb=$(free_space $WORK_FS)
+echo "${free_kb}kb free on /$WORK_FS now."
+
+testname "apply bsdiff patch with low space"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+testname "reapply bsdiff patch with low space"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+# --------------- apply patch with low space on /system and /cache ----------------------
+
+$ADB push $DATA_DIR/old.file $WORK_DIR
+$ADB push $DATA_DIR/patch.bsdiff $WORK_DIR
+
+free_kb=$(free_space $WORK_FS)
+echo "${free_kb}kb free on /$WORK_FS"
+
+run_command mkdir /cache/subdir
+run_command 'echo > /cache/subdir/a.file'
+run_command 'echo > /cache/a.file'
+run_command mkdir /cache/recovery /cache/recovery/otatest
+run_command 'echo > /cache/recovery/otatest/b.file'
+run_command "echo > $CACHE_TEMP_SOURCE"
+free_kb=$(free_space cache)
+echo "${free_kb}kb free on /cache; we'll soon fix that."
+run_command dd if=/dev/zero of=/cache/bloat_small.dat count=128 bs=1024 || fail
+run_command dd if=/dev/zero of=/cache/bloat_large.dat count=$((free_kb-640)) bs=1024 || fail
+free_kb=$(free_space cache)
+echo "${free_kb}kb free on /cache now."
+
+testname "apply bsdiff patch with low space, full cache, can't delete enough"
+$ADB shell 'cat >> /cache/bloat_large.dat' & open_pid=$!
+echo "open_pid is $open_pid"
+
+# size check should fail even though it deletes some stuff
+run_command $WORK_DIR/applypatch -s $NEW_SIZE && fail
+run_command ls /cache/bloat_small.dat && fail # was deleted
+run_command ls /cache/a.file && fail # was deleted
+run_command ls /cache/recovery/otatest/b.file && fail # was deleted
+run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
+run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
+run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
+
+# should fail; not enough files can be deleted
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff && fail
+run_command ls /cache/bloat_large.dat || fail # wasn't deleted because it was open
+run_command ls /cache/subdir/a.file || fail # wasn't deleted because it's in a subdir
+run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
+
+kill $open_pid # /cache/bloat_large.dat is no longer open
+
+testname "apply bsdiff patch with low space, full cache, can delete enough"
+
+# should succeed after deleting /cache/bloat_large.dat
+run_command $WORK_DIR/applypatch -s $NEW_SIZE || fail
+run_command ls /cache/bloat_large.dat && fail # was deleted
+run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
+run_command ls $CACHE_TEMP_SOURCE || fail # wasn't deleted because it's the source file copy
+
+# should succeed
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+run_command ls /cache/subdir/a.file || fail # still wasn't deleted because it's in a subdir
+run_command ls $CACHE_TEMP_SOURCE && fail # was deleted because patching overwrote it, then deleted it
+
+# --------------- apply patch from cache ----------------------
+
+$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
+# put some junk in the old file
+run_command dd if=/dev/urandom of=$WORK_DIR/old.file count=100 bs=1024 || fail
+
+testname "apply bsdiff patch from cache (corrupted source) with low space"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+$ADB push $DATA_DIR/old.file $CACHE_TEMP_SOURCE
+# remove the old file entirely
+run_command rm $WORK_DIR/old.file
+
+testname "apply bsdiff patch from cache (missing source) with low space"
+run_command $WORK_DIR/applypatch $WORK_DIR/old.file $NEW_SHA1 $NEW_SIZE $BAD1_SHA1:$WORK_DIR/foo $OLD_SHA1:$WORK_DIR/patch.bsdiff || fail
+$ADB pull $WORK_DIR/old.file $tmpdir/patched
+diff -q $DATA_DIR/new.file $tmpdir/patched || fail
+
+
+# --------------- cleanup ----------------------
+
+# not necessary if we're about to kill the emulator, but nice for
+# running on real devices or already-running emulators.
+run_command rm /cache/bloat*.dat $WORK_DIR/bloat.dat $CACHE_TEMP_SOURCE $WORK_DIR/old.file $WORK_DIR/patch.xdelta3 $WORK_DIR/patch.bsdiff $WORK_DIR/applypatch
+
+kill $pid_emulator
+
+rm -rf $tmpdir
+
+echo
+echo PASS
+echo
+
diff --git a/tools/applypatch/bsdiff.c b/tools/applypatch/bsdiff.c
new file mode 100644
index 000000000..a2851f952
--- /dev/null
+++ b/tools/applypatch/bsdiff.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// This file is a nearly line-for-line copy of bspatch.c from the
+// bsdiff-4.3 distribution; the primary differences being how the
+// input and output data are read and the error handling. Running
+// applypatch with the -l option will display the bsdiff license
+// notice.
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <bzlib.h>
+
+#include "mincrypt/sha.h"
+
+void ShowBSDiffLicense() {
+ puts("The bsdiff library used herein is:\n"
+ "\n"
+ "Copyright 2003-2005 Colin Percival\n"
+ "All rights reserved\n"
+ "\n"
+ "Redistribution and use in source and binary forms, with or without\n"
+ "modification, are permitted providing that the following conditions\n"
+ "are met:\n"
+ "1. Redistributions of source code must retain the above copyright\n"
+ " notice, this list of conditions and the following disclaimer.\n"
+ "2. Redistributions in binary form must reproduce the above copyright\n"
+ " notice, this list of conditions and the following disclaimer in the\n"
+ " documentation and/or other materials provided with the distribution.\n"
+ "\n"
+ "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n"
+ "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\n"
+ "WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n"
+ "ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY\n"
+ "DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n"
+ "DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n"
+ "OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n"
+ "HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n"
+ "STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\n"
+ "IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\n"
+ "POSSIBILITY OF SUCH DAMAGE.\n"
+ "\n------------------\n\n"
+ "This program uses Julian R Seward's \"libbzip2\" library, available\n"
+ "from http://www.bzip.org/.\n"
+ );
+}
+
+static off_t offtin(u_char *buf)
+{
+ off_t y;
+
+ y=buf[7]&0x7F;
+ y=y*256;y+=buf[6];
+ y=y*256;y+=buf[5];
+ y=y*256;y+=buf[4];
+ y=y*256;y+=buf[3];
+ y=y*256;y+=buf[2];
+ y=y*256;y+=buf[1];
+ y=y*256;y+=buf[0];
+
+ if(buf[7]&0x80) y=-y;
+
+ return y;
+}
+
+int ApplyBSDiffPatch(const unsigned char* old_data, ssize_t old_size,
+ const char* patch_filename,
+ FILE* output, SHA_CTX* ctx) {
+
+ FILE* f;
+ if ((f = fopen(patch_filename, "rb")) == NULL) {
+ fprintf(stderr, "failed to open patch file\n");
+ return 1;
+ }
+
+ // File format:
+ // 0 8 "BSDIFF40"
+ // 8 8 X
+ // 16 8 Y
+ // 24 8 sizeof(newfile)
+ // 32 X bzip2(control block)
+ // 32+X Y bzip2(diff block)
+ // 32+X+Y ??? bzip2(extra block)
+ // with control block a set of triples (x,y,z) meaning "add x bytes
+ // from oldfile to x bytes from the diff block; copy y bytes from the
+ // extra block; seek forwards in oldfile by z bytes".
+
+ unsigned char header[32];
+ if (fread(header, 1, 32, f) < 32) {
+ fprintf(stderr, "failed to read patch file header\n");
+ return 1;
+ }
+
+ if (memcmp(header, "BSDIFF40", 8) != 0) {
+ fprintf(stderr, "corrupt patch file header (magic number)\n");
+ return 1;
+ }
+
+ ssize_t ctrl_len, data_len;
+ ssize_t new_size;
+ ctrl_len = offtin(header+8);
+ data_len = offtin(header+16);
+ new_size = offtin(header+24);
+
+ if (ctrl_len < 0 || data_len < 0 || new_size < 0) {
+ fprintf(stderr, "corrupt patch file header (data lengths)\n");
+ return 1;
+ }
+
+ fclose(f);
+
+ int bzerr;
+
+#define OPEN_AT(f, bzf, offset) \
+ FILE* f; \
+ BZFILE* bzf; \
+ if ((f = fopen(patch_filename, "rb")) == NULL) { \
+ fprintf(stderr, "failed to open patch file\n"); \
+ return 1; \
+ } \
+ if (fseeko(f, offset, SEEK_SET)) { \
+ fprintf(stderr, "failed to seek in patch file\n"); \
+ return 1; \
+ } \
+ if ((bzf = BZ2_bzReadOpen(&bzerr, f, 0, 0, NULL, 0)) == NULL) { \
+ fprintf(stderr, "failed to bzReadOpen in patch file (%d)\n", bzerr); \
+ return 1; \
+ }
+
+ OPEN_AT(cpf, cpfbz2, 32);
+ OPEN_AT(dpf, dpfbz2, 32+ctrl_len);
+ OPEN_AT(epf, epfbz2, 32+ctrl_len+data_len);
+
+#undef OPEN_AT
+
+ unsigned char* new_data = malloc(new_size);
+ if (new_data == NULL) {
+ fprintf(stderr, "failed to allocate memory for output file\n");
+ return 1;
+ }
+
+ off_t oldpos = 0, newpos = 0;
+ off_t ctrl[3];
+ off_t len_read;
+ int i;
+ unsigned char buf[8];
+ while (newpos < new_size) {
+ // Read control data
+ for (i = 0; i < 3; ++i) {
+ len_read = BZ2_bzRead(&bzerr, cpfbz2, buf, 8);
+ if (len_read < 8 || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+ fprintf(stderr, "corrupt patch (read control)\n");
+ return 1;
+ }
+ ctrl[i] = offtin(buf);
+ }
+
+ // Sanity check
+ if (newpos + ctrl[0] > new_size) {
+ fprintf(stderr, "corrupt patch (new file overrun)\n");
+ return 1;
+ }
+
+ // Read diff string
+ len_read = BZ2_bzRead(&bzerr, dpfbz2, new_data + newpos, ctrl[0]);
+ if (len_read < ctrl[0] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+ fprintf(stderr, "corrupt patch (read diff)\n");
+ return 1;
+ }
+
+ // Add old data to diff string
+ for (i = 0; i < ctrl[0]; ++i) {
+ if ((oldpos+i >= 0) && (oldpos+i < old_size)) {
+ new_data[newpos+i] += old_data[oldpos+i];
+ }
+ }
+
+ // Adjust pointers
+ newpos += ctrl[0];
+ oldpos += ctrl[0];
+
+ // Sanity check
+ if (newpos + ctrl[1] > new_size) {
+ fprintf(stderr, "corrupt patch (new file overrun)\n");
+ return 1;
+ }
+
+ // Read extra string
+ len_read = BZ2_bzRead(&bzerr, epfbz2, new_data + newpos, ctrl[1]);
+ if (len_read < ctrl[1] || !(bzerr == BZ_OK || bzerr == BZ_STREAM_END)) {
+ fprintf(stderr, "corrupt patch (read extra)\n");
+ return 1;
+ }
+
+ // Adjust pointers
+ newpos += ctrl[1];
+ oldpos += ctrl[2];
+ }
+
+ BZ2_bzReadClose(&bzerr, cpfbz2);
+ BZ2_bzReadClose(&bzerr, dpfbz2);
+ BZ2_bzReadClose(&bzerr, epfbz2);
+ fclose(cpf);
+ fclose(dpf);
+ fclose(epf);
+
+ if (fwrite(new_data, 1, new_size, output) < new_size) {
+ fprintf(stderr, "short write of output: %d (%s)\n", errno, strerror(errno));
+ return 1;
+ }
+ SHA_update(ctx, new_data, new_size);
+ free(new_data);
+
+ return 0;
+}
diff --git a/tools/applypatch/freecache.c b/tools/applypatch/freecache.c
new file mode 100644
index 000000000..ab71b81cc
--- /dev/null
+++ b/tools/applypatch/freecache.c
@@ -0,0 +1,172 @@
+#include <errno.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+
+#include "applypatch.h"
+
+static int EliminateOpenFiles(char** files, int file_count) {
+ DIR* d;
+ struct dirent* de;
+ d = opendir("/proc");
+ if (d == NULL) {
+ fprintf(stderr, "error opening /proc: %s\n", strerror(errno));
+ return -1;
+ }
+ while ((de = readdir(d)) != 0) {
+ int i;
+ for (i = 0; de->d_name[i] != '\0' && isdigit(de->d_name[i]); ++i);
+ if (de->d_name[i]) continue;
+
+ // de->d_name[i] is numeric
+
+ char path[FILENAME_MAX];
+ strcpy(path, "/proc/");
+ strcat(path, de->d_name);
+ strcat(path, "/fd/");
+
+ DIR* fdd;
+ struct dirent* fdde;
+ fdd = opendir(path);
+ if (fdd == NULL) {
+ fprintf(stderr, "error opening %s: %s\n", path, strerror(errno));
+ continue;
+ }
+ while ((fdde = readdir(fdd)) != 0) {
+ char fd_path[FILENAME_MAX];
+ char link[FILENAME_MAX];
+ strcpy(fd_path, path);
+ strcat(fd_path, fdde->d_name);
+
+ int count;
+ count = readlink(fd_path, link, sizeof(link)-1);
+ if (count >= 0) {
+ link[count] = '\0';
+
+ // This is inefficient, but it should only matter if there are
+ // lots of files in /cache, and lots of them are open (neither
+ // of which should be true, especially in recovery).
+ if (strncmp(link, "/cache/", 7) == 0) {
+ int j;
+ for (j = 0; j < file_count; ++j) {
+ if (files[j] && strcmp(files[j], link) == 0) {
+ printf("%s is open by %s\n", link, de->d_name);
+ free(files[j]);
+ files[j] = NULL;
+ }
+ }
+ }
+ }
+ }
+ closedir(fdd);
+ }
+ closedir(d);
+
+ return 0;
+}
+
+int FindExpendableFiles(char*** names, int* entries) {
+ DIR* d;
+ struct dirent* de;
+ int size = 32;
+ *entries = 0;
+ *names = malloc(size * sizeof(char*));
+
+ char path[FILENAME_MAX];
+
+ // We're allowed to delete unopened regular files in any of these
+ // directories.
+ const char* dirs[2] = {"/cache", "/cache/recovery/otatest"};
+
+ unsigned int i;
+ for (i = 0; i < sizeof(dirs)/sizeof(dirs[0]); ++i) {
+ d = opendir(dirs[i]);
+ if (d == NULL) {
+ fprintf(stderr, "error opening %s: %s\n", dirs[i], strerror(errno));
+ continue;
+ }
+
+ // Look for regular files in the directory (not in any subdirectories).
+ while ((de = readdir(d)) != 0) {
+ strcpy(path, dirs[i]);
+ strcat(path, "/");
+ strcat(path, de->d_name);
+
+ // We can't delete CACHE_TEMP_SOURCE; if it's there we might have
+ // restarted during installation and could be depending on it to
+ // be there.
+ if (strcmp(path, CACHE_TEMP_SOURCE) == 0) continue;
+
+ struct stat st;
+ if (stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
+ if (*entries >= size) {
+ size *= 2;
+ *names = realloc(*names, size * sizeof(char*));
+ }
+ (*names)[(*entries)++] = strdup(path);
+ }
+ }
+
+ closedir(d);
+ }
+
+ printf("%d regular files in deletable directories\n", *entries);
+
+ if (EliminateOpenFiles(*names, *entries) < 0) {
+ return -1;
+ }
+
+ return 0;
+}
+
+int MakeFreeSpaceOnCache(size_t bytes_needed) {
+ size_t free_now = FreeSpaceForFile("/cache");
+ printf("%ld bytes free on /cache (%ld needed)\n",
+ (long)free_now, (long)bytes_needed);
+
+ if (free_now >= bytes_needed) {
+ return 0;
+ }
+
+ char** names;
+ int entries;
+
+ if (FindExpendableFiles(&names, &entries) < 0) {
+ return -1;
+ }
+
+ if (entries == 0) {
+ // nothing we can delete to free up space!
+ fprintf(stderr, "no files can be deleted to free space on /cache\n");
+ return -1;
+ }
+
+ // We could try to be smarter about which files to delete: the
+ // biggest ones? the smallest ones that will free up enough space?
+ // the oldest? the newest?
+ //
+ // Instead, we'll be dumb.
+
+ int i;
+ for (i = 0; i < entries && free_now < bytes_needed; ++i) {
+ if (names[i]) {
+ unlink(names[i]);
+ free_now = FreeSpaceForFile("/cache");
+ printf("deleted %s; now %ld bytes free\n", names[i], (long)free_now);
+ free(names[i]);
+ }
+ }
+
+ for (; i < entries; ++i) {
+ free(names[i]);
+ }
+ free(names);
+
+ return (free_now >= bytes_needed) ? 0 : -1;
+}
diff --git a/tools/applypatch/testdata/new.file b/tools/applypatch/testdata/new.file
new file mode 100644
index 000000000..cdeb8fd50
--- /dev/null
+++ b/tools/applypatch/testdata/new.file
Binary files differ
diff --git a/tools/applypatch/testdata/old.file b/tools/applypatch/testdata/old.file
new file mode 100644
index 000000000..166c8732e
--- /dev/null
+++ b/tools/applypatch/testdata/old.file
Binary files differ
diff --git a/tools/applypatch/testdata/patch.bsdiff b/tools/applypatch/testdata/patch.bsdiff
new file mode 100644
index 000000000..b78d38573
--- /dev/null
+++ b/tools/applypatch/testdata/patch.bsdiff
Binary files differ
diff --git a/tools/apriori/Android.mk b/tools/apriori/Android.mk
new file mode 100644
index 000000000..79ecf7fc7
--- /dev/null
+++ b/tools/apriori/Android.mk
@@ -0,0 +1,61 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for apriori
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(TARGET_SIMULATOR),true)
+include $(CLEAR_VARS)
+
+LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DSUPPORT_ANDROID_PRELINK_TAGS
+LOCAL_CFLAGS += -DDEBUG
+LOCAL_CFLAGS += -DADJUST_ELF=1
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DBIG_ENDIAN=1
+endif
+
+ifeq ($(HOST_OS),darwin)
+LOCAL_CFLAGS += -DFSCANF_IS_BROKEN
+endif
+ifeq ($(HOST_OS),windows)
+LOCAL_CFLAGS += -DFSCANF_IS_BROKEN
+LOCAL_LDLIBS += -lintl
+endif
+
+
+
+LOCAL_SRC_FILES := \
+ apriori.c \
+ cmdline.c \
+ debug.c \
+ hash.c \
+ main.c \
+ prelink_info.c \
+ rangesort.c \
+ source.c \
+ prelinkmap.c
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ external/elfutils/lib/ \
+ external/elfutils/libelf/ \
+ external/elfutils/libebl/ \
+ external/elfcopy/
+
+LOCAL_STATIC_LIBRARIES := libelfcopy libelf libebl #dl
+
+ifeq ($(TARGET_ARCH),arm)
+LOCAL_STATIC_LIBRARIES += libebl_arm
+endif
+
+LOCAL_MODULE := apriori
+
+include $(BUILD_HOST_EXECUTABLE)
+endif # TARGET_SIMULATOR != true
diff --git a/tools/apriori/apriori.c b/tools/apriori/apriori.c
new file mode 100644
index 000000000..d1807b322
--- /dev/null
+++ b/tools/apriori/apriori.c
@@ -0,0 +1,2601 @@
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <libebl.h>
+#ifdef ARM_SPECIFIC_HACKS
+ #include <libebl_arm.h>
+#endif/*ARM_SPECIFIC_HACKS*/
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <hash.h>
+#include <apriori.h>
+#include <source.h>
+#include <tweak.h>
+#include <rangesort.h>
+#include <prelink_info.h>
+#include <prelinkmap.h>
+#include <libgen.h>
+
+#ifndef ADJUST_ELF
+#error "ADJUST_ELF must be defined!"
+#endif
+
+/* When this macro is defined, apriori sets to ZERO those relocation values for
+ which it canot find the appropriate referent.
+*/
+#define PERMISSIVE
+#define COPY_SECTION_DATA_BUFFER (0)
+/* When this macro is set to a nonzero value, we replace calls to elf_strptr()
+ on the target ELF handle with code that extracts the strings directly from
+ the data buffers of that ELF handle. In this case, elf_strptr() does not
+ work as expected, as it tries to read the data buffer of the associated
+ string section directly from the file, and that buffer does not exist yet
+ in the file, since we haven't committed our changes yet.
+*/
+#define ELF_STRPTR_IS_BROKEN (1)
+
+/* When the macro below is defined, apriori does not mark for removal those
+ relocation sections that it fully handles. Instead, apriori just sets their
+ sizes to zero. This is more for debugging than of any actual use.
+
+ This macro is meaningful only when ADJUST_ELF!=0
+*/
+#define REMOVE_HANDLED_SECTIONS
+
+extern int verbose_flag;
+
+static source_t *sources = NULL;
+
+#if defined(DEBUG) && 0
+
+static void print_shdr(source_t *source, Elf_Scn *scn)
+{
+ GElf_Shdr shdr_mem, *shdr;
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ Elf_Data *data = elf_getdata(scn, NULL);
+ INFO("\t%02d: data = %p, hdr = { offset = %8lld, size = %lld }, "
+ "data->d_buf = %p data->d_off = %lld, data->d_size = %d\n",
+ elf_ndxscn(scn),
+ data,
+ shdr->sh_offset, shdr->sh_size,
+ data->d_buf, data->d_off, data->d_size);
+}
+
+static void print_shdr_idx(source_t *source, Elf *elf, int idx)
+{
+ print_shdr(source, elf_getscn(elf, idx));
+}
+
+static void print_shdrs(source_t *source) {
+ Elf_Scn *scn = NULL;
+ INFO("section offset dump for new ELF\n");
+ while ((scn = elf_nextscn (source->elf, scn)) != NULL)
+ print_shdr(source, scn);
+
+ INFO("\nsection offset dump for original ELF\n");
+ while ((scn = elf_nextscn (source->oldelf, scn)) != NULL)
+ print_shdr(source, scn);
+
+#if 0
+ {
+ INFO("section offset dump for new ELF\n");
+ int i = 0;
+ for (i = 0; i < source->shnum; i++) {
+ scn = elf_getscn(source->elf, i);
+ print_shdr(source, scn);
+ }
+ }
+#endif
+}
+
+#endif /* DEBUG */
+
+static char * find_file(const char *libname,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs);
+
+static inline source_t* find_source(const char *name,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs) {
+ char *full = find_file(name, lib_lookup_dirs, num_lib_lookup_dirs);
+ if (full) {
+ source_t *trav = sources;
+ while (trav) {
+ if (!strcmp(trav->name, full))
+ break;
+ trav = trav->next;
+ }
+ free(full);
+ return trav;
+ }
+ return NULL;
+}
+
+static inline void add_to_sources(source_t *src) {
+ src->next = sources;
+ sources = src;
+}
+
+static void handle_range_error(range_error_t err,
+ range_t *left, range_t *right) {
+ switch (err) {
+ case ERROR_CONTAINS:
+ ERROR("ERROR: section (%lld, %lld bytes) contains "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ case ERROR_OVERLAPS:
+ ERROR("ERROR: Section (%lld, %lld bytes) intersects "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ default:
+ ASSERT(!"Unknown range error code!");
+ }
+
+ FAILIF(1, "Range error.\n");
+}
+
+static void create_elf_sections(source_t *source, Elf *elf)
+{
+ INFO("Creating new ELF sections.\n");
+ ASSERT(elf == NULL || source->elf == NULL || source->elf == elf);
+ if (elf == NULL) {
+ ASSERT(source->elf != NULL);
+ elf = source->elf;
+ }
+
+ int cnt = 1;
+ Elf_Scn *oldscn = NULL, *scn;
+ while ((oldscn = elf_nextscn (source->oldelf, oldscn)) != NULL) {
+ GElf_Shdr *oldshdr, oldshdr_mem;
+
+ scn = elf_newscn(elf);
+ FAILIF_LIBELF(NULL == scn, elf_newscn);
+
+ oldshdr = gelf_getshdr(oldscn, &oldshdr_mem);
+ FAILIF_LIBELF(NULL == oldshdr, gelf_getshdr);
+ /* Set the section header of the new section to be the same as the
+ headset of the old section by default. */
+ gelf_update_shdr(scn, oldshdr);
+
+ /* Copy the section data */
+ Elf_Data *olddata = elf_getdata(oldscn, NULL);
+ FAILIF_LIBELF(NULL == olddata, elf_getdata);
+
+ Elf_Data *data = elf_newdata(scn);
+ FAILIF_LIBELF(NULL == data, elf_newdata);
+ *data = *olddata;
+#if COPY_SECTION_DATA_BUFFER
+ if (olddata->d_buf != NULL) {
+ data->d_buf = MALLOC(data->d_size);
+ memcpy(data->d_buf, olddata->d_buf, olddata->d_size);
+ }
+#endif
+
+ INFO("\tsection %02d: [%-30s] created\n",
+ cnt,
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ oldshdr->sh_name));
+
+ if (ADJUST_ELF) {
+ ASSERT(source->shdr_info != NULL);
+ /* Create a new section. */
+ source->shdr_info[cnt].idx = cnt;
+ source->shdr_info[cnt].newscn = scn;
+ source->shdr_info[cnt].data = data;
+ source->shdr_info[cnt].
+ use_old_shdr_for_relocation_calculations = 1;
+ INFO("\tsection [%s] (old offset %lld, old size %lld) "
+ "will have index %d (was %d).\n",
+ source->shdr_info[cnt].name,
+ source->shdr_info[cnt].old_shdr.sh_offset,
+ source->shdr_info[cnt].old_shdr.sh_size,
+ source->shdr_info[cnt].idx,
+ elf_ndxscn(source->shdr_info[cnt].scn));
+ /* Same as the next assert */
+ ASSERT(elf_ndxscn (source->shdr_info[cnt].newscn) ==
+ source->shdr_info[cnt].idx);
+ }
+
+ ASSERT(elf_ndxscn(scn) == (size_t)cnt);
+ cnt++;
+ }
+}
+
+/* This function sets up the shdr_info[] array of a source_t. We call it only
+ when ADJUST_ELF is non-zero (i.e., support for adjusting an ELF file for
+ changes in sizes and numbers of relocation sections is compiled in. Note
+ that setup_shdr_info() depends only on the information in source->oldelf,
+ not on source->elf.
+*/
+
+static void setup_shdr_info(source_t *source)
+{
+ if (ADJUST_ELF)
+ {
+ /* Allocate the section-header-info buffer. */
+ INFO("Allocating section-header info structure (%d) bytes...\n",
+ source->shnum * sizeof (shdr_info_t));
+
+ source->shdr_info = (shdr_info_t *)CALLOC(source->shnum,
+ sizeof (shdr_info_t));
+
+ /* Mark the SHT_NULL section as handled. */
+ source->shdr_info[0].idx = 2;
+
+ int cnt = 1;
+ Elf_Scn *oldscn = NULL;
+ while ((oldscn = elf_nextscn (source->oldelf, oldscn)) != NULL) {
+ /* Copy the section header */
+ ASSERT(elf_ndxscn(oldscn) == (size_t)cnt);
+
+ /* Initialized the corresponding shdr_info entry */
+ {
+ /* Mark the section with a non-zero index. Later, when we
+ decide to drop a section, we will set its idx to zero, and
+ assign section numbers to the remaining sections.
+ */
+ source->shdr_info[cnt].idx = 1;
+
+ source->shdr_info[cnt].scn = oldscn;
+
+ /* NOTE: Here we pupulate the section-headset struct with the
+ same values as the original section's. After the
+ first run of prelink(), we will update the sh_size
+ fields of those sections that need resizing.
+ */
+ FAILIF_LIBELF(NULL ==
+ gelf_getshdr(oldscn,
+ &source->shdr_info[cnt].shdr),
+ gelf_getshdr);
+
+ /* Get the name of the section. */
+ source->shdr_info[cnt].name =
+ elf_strptr (source->oldelf, source->shstrndx,
+ source->shdr_info[cnt].shdr.sh_name);
+
+ INFO("\tname: %s\n", source->shdr_info[cnt].name);
+ FAILIF(source->shdr_info[cnt].name == NULL,
+ "Malformed file: section %d name is null\n",
+ cnt);
+
+ /* Remember the shdr.sh_link value. We need to remember this
+ value for those sections that refer to other sections. For
+ example, we need to remember it for relocation-entry
+ sections, because if we modify the symbol table that a
+ relocation-entry section is relative to, then we need to
+ patch the relocation section. By the time we get to
+ deciding whether we need to patch the relocation section, we
+ will have overwritten its header's sh_link field with a new
+ value.
+ */
+ source->shdr_info[cnt].old_shdr = source->shdr_info[cnt].shdr;
+ INFO("\t\toriginal sh_link: %08d\n",
+ source->shdr_info[cnt].old_shdr.sh_link);
+ INFO("\t\toriginal sh_addr: %lld\n",
+ source->shdr_info[cnt].old_shdr.sh_addr);
+ INFO("\t\toriginal sh_offset: %lld\n",
+ source->shdr_info[cnt].old_shdr.sh_offset);
+ INFO("\t\toriginal sh_size: %lld\n",
+ source->shdr_info[cnt].old_shdr.sh_size);
+
+ FAILIF(source->shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX,
+ "Cannot handle sh_type SHT_SYMTAB_SHNDX!\n");
+ FAILIF(source->shdr_info[cnt].shdr.sh_type == SHT_GROUP,
+ "Cannot handle sh_type SHT_GROUP!\n");
+ FAILIF(source->shdr_info[cnt].shdr.sh_type == SHT_GNU_versym,
+ "Cannot handle sh_type SHT_GNU_versym!\n");
+ }
+
+ cnt++;
+ } /* for each section */
+ } /* if (ADJUST_ELF) */
+}
+
+static Elf * init_elf(source_t *source, bool create_new_sections)
+{
+ Elf *elf;
+ if (source->output != NULL) {
+ if (source->output_is_dir) {
+ source->output_is_dir++;
+ char *dir = source->output;
+ int dirlen = strlen(dir);
+ /* The main() function maintains a pointer to source->output; it
+ frees the buffer after apriori() returns.
+ */
+ source->output = MALLOC(dirlen +
+ 1 + /* slash */
+ strlen(source->name) +
+ 1); /* null terminator */
+ strcpy(source->output, dir);
+ source->output[dirlen] = '/';
+ strcpy(source->output + dirlen + 1,
+ basename(source->name));
+ }
+
+ source->newelf_fd = open(source->output,
+ O_RDWR | O_CREAT,
+ 0666);
+ FAILIF(source->newelf_fd < 0, "open(%s): %s (%d)\n",
+ source->output,
+ strerror(errno),
+ errno);
+ elf = elf_begin(source->newelf_fd, ELF_C_WRITE, NULL);
+ FAILIF_LIBELF(elf == NULL, elf_begin);
+ } else {
+ elf = elf_clone(source->oldelf, ELF_C_EMPTY);
+ FAILIF_LIBELF(elf == NULL, elf_clone);
+ }
+
+ GElf_Ehdr *oldehdr = gelf_getehdr(source->oldelf, &source->old_ehdr_mem);
+ FAILIF_LIBELF(NULL == oldehdr, gelf_getehdr);
+
+ /* Create new ELF and program headers for the elf file */
+ INFO("Creating empty ELF and program headers...\n");
+ FAILIF_LIBELF(gelf_newehdr (elf, gelf_getclass (source->oldelf)) == 0,
+ gelf_newehdr);
+ FAILIF_LIBELF(oldehdr->e_type != ET_REL
+ && gelf_newphdr (elf,
+ oldehdr->e_phnum) == 0,
+ gelf_newphdr);
+
+ /* Copy the elf header */
+ INFO("Copying ELF header...\n");
+ GElf_Ehdr *ehdr = gelf_getehdr(elf, &source->ehdr_mem);
+ FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
+ memcpy(ehdr, oldehdr, sizeof(GElf_Ehdr));
+ FAILIF_LIBELF(!gelf_update_ehdr(elf, ehdr), gelf_update_ehdr);
+
+ /* Copy out the old program header: notice that if the ELF file does not
+ have a program header, this loop won't execute.
+ */
+ INFO("Copying ELF program header...\n");
+ {
+ int cnt;
+ source->phdr_info = (GElf_Phdr *)CALLOC(ehdr->e_phnum,
+ sizeof(GElf_Phdr));
+ for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) {
+ INFO("\tRetrieving entry %d\n", cnt);
+ FAILIF_LIBELF(NULL ==
+ gelf_getphdr(source->oldelf, cnt,
+ source->phdr_info + cnt),
+ gelf_getphdr);
+ FAILIF_LIBELF(gelf_update_phdr (elf, cnt,
+ source->phdr_info + cnt) == 0,
+ gelf_update_phdr);
+ }
+ }
+
+ /* Copy the sections and the section headers. */
+ if (create_new_sections)
+ {
+ create_elf_sections(source, elf);
+ }
+
+ /* The ELF library better follows our layout when this is not a
+ relocatable object file. */
+ elf_flagelf (elf, ELF_C_SET, (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0));
+
+ return elf;
+}
+
+static shdr_info_t *lookup_shdr_info_by_new_section(
+ source_t *source,
+ const char *sname,
+ Elf_Scn *newscn)
+{
+ if (source->shdr_info == NULL) return NULL;
+ int cnt;
+ for (cnt = 0; cnt < source->shnum; cnt++) {
+ if (source->shdr_info[cnt].newscn == newscn) {
+ INFO("\t\tnew section at %p matches shdr_info[%d], "
+ "section [%s]!\n",
+ newscn,
+ cnt,
+ source->shdr_info[cnt].name);
+ FAILIF(strcmp(sname, source->shdr_info[cnt].name),
+ "Matched section's name [%s] does not match "
+ "looked-up section's name [%s]!\n",
+ source->shdr_info[cnt].name,
+ sname);
+ return source->shdr_info + cnt;
+ }
+ }
+ return NULL;
+}
+
+static bool do_init_source(source_t *source, unsigned base)
+{
+ /* Find various sections. */
+ size_t scnidx;
+ Elf_Scn *scn;
+ GElf_Shdr *shdr, shdr_mem;
+ source->sorted_sections = init_range_list();
+ INFO("Processing [%s]'s sections...\n", source->name);
+ for (scnidx = 1; scnidx < (size_t)source->shnum; scnidx++) {
+ INFO("\tGetting section index %d...\n", scnidx);
+ scn = elf_getscn(source->elf, scnidx);
+ if (NULL == scn) {
+ /* If we get an error from elf_getscn(), it means that a section
+ at the requested index does not exist. This may happen when
+ we remove sections. Since we do not update source->shnum
+ (we can't, since we need to know the original number of sections
+ to know source->shdr_info[]'s length), we will attempt to
+ retrieve a section for an index that no longer exists in the
+ new ELF file. */
+ INFO("\tThere is no section at index %d anymore, continuing.\n",
+ scnidx);
+ continue;
+ }
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
+
+ /* We haven't modified the shstrtab section, and so shdr->sh_name
+ has the same value as before. Thus we look up the name based
+ on the old ELF handle. We cannot use shstrndx on the new ELF
+ handle because the index of the shstrtab section may have
+ changed (and calling elf_getshstrndx() returns the same section
+ index, so libelf can't handle thise ither).
+ */
+ const char *sname =
+ elf_strptr(source->oldelf, source->shstrndx, shdr->sh_name);
+ ASSERT(sname);
+
+ INFO("\tAdding [%s] (%lld, %lld)...\n",
+ sname,
+ shdr->sh_addr,
+ shdr->sh_addr + shdr->sh_size);
+ if ((shdr->sh_flags & SHF_ALLOC) == SHF_ALLOC) {
+ add_unique_range_nosort(source->sorted_sections,
+ shdr->sh_addr,
+ shdr->sh_size,
+ scn,
+ handle_range_error,
+ NULL); /* no user-data destructor */
+ }
+
+ if (shdr->sh_type == SHT_DYNSYM) {
+ source->symtab.scn = scn;
+ source->symtab.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
+ memcpy(&source->symtab.shdr, shdr, sizeof(GElf_Shdr));
+ source->symtab.info = lookup_shdr_info_by_new_section(
+ source, sname, scn);
+ ASSERT(source->shdr_info == NULL || source->symtab.info != NULL);
+
+ /* The sh_link field of the section header of the symbol table
+ contains the index of the associated strings table. */
+ source->strtab.scn = elf_getscn(source->elf,
+ source->symtab.shdr.sh_link);
+ FAILIF_LIBELF(NULL == source->strtab.scn, elf_getscn);
+ FAILIF_LIBELF(NULL == gelf_getshdr(source->strtab.scn,
+ &source->strtab.shdr),
+ gelf_getshdr);
+ source->strtab.data = elf_getdata(source->strtab.scn, NULL);
+ FAILIF_LIBELF(NULL == source->strtab.data, elf_getdata);
+ source->strtab.info = lookup_shdr_info_by_new_section(
+ source,
+ elf_strptr(source->oldelf, source->shstrndx,
+ source->strtab.shdr.sh_name),
+ source->strtab.scn);
+ ASSERT(source->shdr_info == NULL || source->strtab.info != NULL);
+ } else if (shdr->sh_type == SHT_DYNAMIC) {
+ source->dynamic.scn = scn;
+ source->dynamic.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->dynamic.data, elf_getdata);
+ memcpy(&source->dynamic.shdr, shdr, sizeof(GElf_Shdr));
+ source->dynamic.info = lookup_shdr_info_by_new_section(
+ source, sname, scn);
+ ASSERT(source->shdr_info == NULL || source->dynamic.info != NULL);
+ } else if (shdr->sh_type == SHT_HASH) {
+ source->hash.scn = scn;
+ source->hash.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->hash.data, elf_getdata);
+ memcpy(&source->hash.shdr, shdr, sizeof(GElf_Shdr));
+ source->hash.info = lookup_shdr_info_by_new_section(
+ source, sname, scn);
+ ASSERT(source->shdr_info == NULL || source->hash.info != NULL);
+ } else if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
+ if (source->num_relocation_sections ==
+ source->relocation_sections_size) {
+ source->relocation_sections_size += 5;
+ source->relocation_sections =
+ (section_info_t *)REALLOC(source->relocation_sections,
+ source->relocation_sections_size *
+ sizeof(section_info_t));
+ }
+ section_info_t *reloc =
+ source->relocation_sections + source->num_relocation_sections;
+ reloc->scn = scn;
+ reloc->info = lookup_shdr_info_by_new_section(source, sname, scn);
+ ASSERT(source->shdr_info == NULL || reloc->info != NULL);
+ reloc->data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == reloc->data, elf_getdata);
+ memcpy(&reloc->shdr, shdr, sizeof(GElf_Shdr));
+ source->num_relocation_sections++;
+ } else if (!strcmp(sname, ".bss")) {
+ source->bss.scn = scn;
+ source->bss.data = elf_getdata(scn, NULL);
+ source->bss.info = lookup_shdr_info_by_new_section(
+ source, sname, scn);
+ ASSERT(source->shdr_info == NULL || source->bss.info != NULL);
+ /* The BSS section occupies no space in the ELF file. */
+ FAILIF_LIBELF(NULL == source->bss.data, elf_getdata)
+ FAILIF(NULL != source->bss.data->d_buf,
+ "Enexpected: section [%s] has data!",
+ sname);
+ memcpy(&source->bss.shdr, shdr, sizeof(GElf_Shdr));
+ }
+ }
+ sort_ranges(source->sorted_sections);
+
+ source->unfinished =
+ (unfinished_relocation_t *)CALLOC(source->num_relocation_sections,
+ sizeof(unfinished_relocation_t));
+
+ if (source->dynamic.scn == NULL) {
+ INFO("File [%s] does not have a dynamic section!\n", source->name);
+ /* If this is a static executable, we won't update anything. */
+ source->dry_run = 1;
+ return false;
+ }
+
+ FAILIF(source->symtab.scn == NULL,
+ "File [%s] does not have a dynamic symbol table!\n",
+ source->name);
+ FAILIF(source->hash.scn == NULL,
+ "File [%s] does not have a hash table!\n",
+ source->name);
+ FAILIF(source->hash.shdr.sh_link != elf_ndxscn(source->symtab.scn),
+ "Hash points to section %d, not to %d as expected!\n",
+ source->hash.shdr.sh_link,
+ elf_ndxscn(source->symtab.scn));
+
+ /* Now, find out how many symbols we have and allocate the array of
+ satisfied symbols.
+
+ NOTE: We don't count the number of undefined symbols here; we will
+ iterate over the symbol table later, and count them then, when it is
+ more convenient.
+ */
+ size_t symsize = gelf_fsize (source->elf,
+ ELF_T_SYM,
+ 1, source->elf_hdr.e_version);
+ ASSERT(symsize);
+
+ source->num_syms = source->symtab.data->d_size / symsize;
+ source->base = (source->oldelf_hdr.e_type == ET_DYN) ? base : 0;
+ INFO("Relink base for [%s]: 0x%lx\n", source->name, source->base);
+ FAILIF(source->base == -1,
+ "Can't prelink [%s]: it's a shared library and you did not "
+ "provide a prelink address!\n",
+ source->name);
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ FAILIF(source->prelinked && source->base != source->prelink_base,
+ "ERROR: file [%s] has already been prelinked for 0x%08lx. "
+ "Cannot change to 0x%08lx!\n",
+ source->name,
+ source->prelink_base,
+ source->base);
+#endif/*SUPPORT_ANDROID_PRELINK_TAGS*/
+
+ return true;
+}
+
+static source_t* init_source(const char *full_path,
+ const char *output, int is_file,
+ int base, int dry_run)
+{
+ source_t *source = (source_t *)CALLOC(1, sizeof(source_t));
+
+ ASSERT(full_path);
+ source->name = full_path;
+ source->output = output;
+ source->output_is_dir = !is_file;
+
+ source->newelf_fd = -1;
+ source->elf_fd = -1;
+ INFO("Opening %s...\n", full_path);
+ source->elf_fd =
+ open(full_path, ((dry_run || output != NULL) ? O_RDONLY : O_RDWR));
+ FAILIF(source->elf_fd < 0, "open(%s): %s (%d)\n",
+ full_path,
+ strerror(errno),
+ errno);
+
+ FAILIF(fstat(source->elf_fd, &source->elf_file_info) < 0,
+ "fstat(%s(fd %d)): %s (%d)\n",
+ source->name,
+ source->elf_fd,
+ strerror(errno),
+ errno);
+ INFO("File [%s]'s size is %lld bytes!\n",
+ source->name,
+ source->elf_file_info.st_size);
+
+ INFO("Calling elf_begin(%s)...\n", full_path);
+
+ source->oldelf =
+ elf_begin(source->elf_fd,
+ (dry_run || output != NULL) ? ELF_C_READ : ELF_C_RDWR,
+ NULL);
+ FAILIF_LIBELF(source->oldelf == NULL, elf_begin);
+
+ /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
+ if(elf_kind(source->oldelf) != ELF_K_ELF) {
+ ERROR("Input file %s is not in ELF format!\n", full_path);
+ return NULL;
+ }
+
+ /* Make sure this is a shared library or an executable. */
+ {
+ INFO("Making sure %s is a shared library or an executable...\n",
+ full_path);
+ FAILIF_LIBELF(0 == gelf_getehdr(source->oldelf, &source->oldelf_hdr),
+ gelf_getehdr);
+ FAILIF(source->oldelf_hdr.e_type != ET_DYN &&
+ source->oldelf_hdr.e_type != ET_EXEC,
+ "%s must be a shared library (elf type is %d, expecting %d).\n",
+ full_path,
+ source->oldelf_hdr.e_type,
+ ET_DYN);
+ }
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ /* First, check to see if the file has been prelinked. */
+ source->prelinked =
+ check_prelinked(source->name,
+ source->oldelf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
+ &source->prelink_base);
+ /* Note that in the INFO() below we need to use oldelf_hdr because we
+ haven't cloned the ELF file yet, and source->elf_hdr is not defined. */
+ if (source->prelinked) {
+ PRINT("%s [%s] is already prelinked at 0x%08lx!\n",
+ (source->oldelf_hdr.e_type == ET_EXEC ?
+ "Executable" : "Shared library"),
+ source->name,
+ source->prelink_base);
+ /* Force a dry run when the file has already been prelinked */
+ source->dry_run = dry_run = 1;
+ }
+ else {
+ INFO("%s [%s] is not prelinked!\n",
+ (source->oldelf_hdr.e_type == ET_EXEC ?
+ "Executable" : "Shared library"),
+ source->name);
+ source->dry_run = dry_run;
+ }
+#endif/*SUPPORT_ANDROID_PRELINK_TAGS*/
+
+ /* Get the index of the section-header-strings-table section. */
+ FAILIF_LIBELF(elf_getshstrndx (source->oldelf, &source->shstrndx) < 0,
+ elf_getshstrndx);
+
+ FAILIF_LIBELF(elf_getshnum (source->oldelf, (size_t *)&source->shnum) < 0,
+ elf_getshnum);
+
+ /* When we have a dry run, or when ADJUST_ELF is enabled, we use
+ source->oldelf for source->elf, because the former is mmapped privately,
+ so changes to it have no effect. With ADJUST_ELF, the first run of
+ prelink() is a dry run. We will reopen the elf file for write access
+ after that dry run, before we call adjust_elf. */
+
+ source->elf = (ADJUST_ELF || source->dry_run) ?
+ source->oldelf : init_elf(source, ADJUST_ELF == 0);
+
+ FAILIF_LIBELF(0 == gelf_getehdr(source->elf, &source->elf_hdr),
+ gelf_getehdr);
+#ifdef DEBUG
+ ASSERT(!memcmp(&source->oldelf_hdr,
+ &source->elf_hdr,
+ sizeof(source->elf_hdr)));
+#endif
+
+ /* Get the EBL handling. The -g option is currently the only reason
+ we need EBL so dont open the backend unless necessary. */
+ source->ebl = ebl_openbackend (source->elf);
+ FAILIF_LIBELF(NULL == source->ebl, ebl_openbackend);
+#ifdef ARM_SPECIFIC_HACKS
+ FAILIF_LIBELF(0 != arm_init(source->elf, source->elf_hdr.e_machine,
+ source->ebl, sizeof(Ebl)),
+ arm_init);
+#endif/*ARM_SPECIFIC_HACKS*/
+
+ add_to_sources(source);
+ if (do_init_source(source, base) == false) return NULL;
+ return source;
+}
+
+/* complements do_init_source() */
+static void do_destroy_source(source_t *source)
+{
+ int cnt;
+ destroy_range_list(source->sorted_sections);
+ source->sorted_sections = NULL;
+ for (cnt = 0; cnt < source->num_relocation_sections; cnt++) {
+ FREEIF(source->unfinished[cnt].rels);
+ source->unfinished[cnt].rels = NULL;
+ source->unfinished[cnt].num_rels = 0;
+ source->unfinished[cnt].rels_size = 0;
+ }
+ if (source->jmprel.sections != NULL) {
+ destroy_range_list(source->jmprel.sections);
+ source->jmprel.sections = NULL;
+ }
+ if (source->rel.sections != NULL) {
+ destroy_range_list(source->rel.sections);
+ source->rel.sections = NULL;
+ }
+ FREE(source->unfinished); /* do_init_source() */
+ source->unfinished = NULL;
+ FREE(source->relocation_sections); /* do_init_source() */
+ source->relocation_sections = NULL;
+ source->num_relocation_sections = source->relocation_sections_size = 0;
+}
+
+static void destroy_source(source_t *source)
+{
+ /* Is this a little-endian ELF file? */
+ if (source->oldelf != source->elf) {
+ /* If it's a dynamic executable, this must not be a dry run. */
+ if (!source->dry_run && source->dynamic.scn != NULL)
+ {
+ FAILIF_LIBELF(elf_update(source->elf, ELF_C_WRITE) == -1,
+ elf_update);
+ }
+ FAILIF_LIBELF(elf_end(source->oldelf), elf_end);
+ }
+ ebl_closebackend(source->ebl);
+ FAILIF_LIBELF(elf_end(source->elf), elf_end);
+ FAILIF(close(source->elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+ source->name, strerror(errno), errno);
+ FAILIF((source->newelf_fd >= 0) && (close(source->newelf_fd) < 0),
+ "Could not close output file: %s (%d)!\n", strerror(errno), errno);
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ if (!source->dry_run) {
+ if (source->dynamic.scn != NULL &&
+ source->elf_hdr.e_type != ET_EXEC)
+ {
+ /* For some reason, trying to write directly to source->elf_fd
+ causes a "bad file descriptor" error because of something libelf
+ does. We just close the file descriptor and open a new one in
+ function setup_prelink_info() below. */
+ INFO("%s: setting up prelink tag at end of file.\n",
+ source->output ? source->output : source->name);
+ setup_prelink_info(source->output ? source->output : source->name,
+ source->elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
+ source->base);
+ }
+ else INFO("%s: executable, NOT setting up prelink tag.\n",
+ source->name);
+ }
+#endif/*SUPPORT_ANDROID_PRELINK_TAGS*/
+
+ do_destroy_source(source);
+
+ if (source->shstrtab_data != NULL)
+ FREEIF(source->shstrtab_data->d_buf); /* adjust_elf */
+
+ FREE(source->lib_deps); /* list of library dependencies (process_file()) */
+ FREEIF(source->shdr_info); /* setup_shdr_info() */
+ FREEIF(source->phdr_info); /* init_elf() */
+ FREE(source->name); /* assigned to by init_source() */
+ /* If the output is a directory, in init_elf() we allocate a buffer where
+ we copy the directory, a slash, and the file name. Here we free that
+ buffer.
+ */
+ if (source->output_is_dir > 1) {
+ FREE(source->output);
+ }
+ FREE(source); /* init_source() */
+}
+
+static void reinit_source(source_t *source)
+{
+ do_destroy_source(source);
+ do_init_source(source, source->base);
+
+ {
+ /* We've gathered all the DT_DYNAMIC entries; now we need to figure
+ out which relocation sections fit in which range as described by
+ the entries. Before we do so, however, we will populate the
+ jmprel and rel members of source, as well as their sizes.
+ */
+
+ size_t dynidx, numdyn;
+ GElf_Dyn *dyn, dyn_mem;
+
+ numdyn = source->dynamic.shdr.sh_size /
+ source->dynamic.shdr.sh_entsize;
+
+ source->rel.idx = source->rel.sz_idx = -1;
+ source->jmprel.idx = source->jmprel.sz_idx = -1;
+ for (dynidx = 0; dynidx < numdyn; dynidx++) {
+ dyn = gelf_getdyn (source->dynamic.data,
+ dynidx,
+ &dyn_mem);
+ FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
+ switch (dyn->d_tag)
+ {
+ case DT_NEEDED:
+ break;
+ case DT_JMPREL:
+ INFO("reinit_source: DT_JMPREL is at index %d, 0x%08llx.\n",
+ dynidx, dyn->d_un.d_ptr);
+ source->jmprel.idx = dynidx;
+ source->jmprel.addr = dyn->d_un.d_ptr;
+ break;
+ case DT_PLTRELSZ:
+ INFO("reinit_source: DT_PLTRELSZ is at index %d, 0x%08llx.\n",
+ dynidx, dyn->d_un.d_val);
+ source->jmprel.sz_idx = dynidx;
+ source->jmprel.size = dyn->d_un.d_val;
+ break;
+ case DT_REL:
+ INFO("reinit_source: DT_REL is at index %d, 0x%08llx.\n",
+ dynidx, dyn->d_un.d_ptr);
+ source->rel.idx = dynidx;
+ source->rel.addr = dyn->d_un.d_ptr;
+ break;
+ case DT_RELSZ:
+ INFO("reinit_source: DT_RELSZ is at index %d, 0x%08llx.\n",
+ dynidx, dyn->d_un.d_val);
+ source->rel.sz_idx = dynidx;
+ source->rel.size = dyn->d_un.d_val;
+ break;
+ case DT_RELA:
+ case DT_RELASZ:
+ FAILIF(1, "Can't handle DT_RELA and DT_RELASZ entries!\n");
+ break;
+ } /* switch */
+ } /* for each dynamic entry... */
+ }
+}
+
+static GElf_Sym *hash_lookup_global_or_weak_symbol(source_t *lib,
+ const char *symname,
+ GElf_Sym *lib_sym_mem)
+{
+ int lib_symidx = hash_lookup(lib->elf,
+ lib->hash.data,
+ lib->symtab.data,
+ lib->strtab.data,
+ symname);
+
+ GElf_Sym sym_mem;
+ if (SHN_UNDEF != lib_symidx) {
+ /* We found the symbol--now check to see if it is global
+ or weak. If this is the case, then the symbol satisfies
+ the dependency. */
+ GElf_Sym *lib_sym = gelf_getsymshndx(lib->symtab.data,
+ NULL,
+ lib_symidx,
+ &sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == lib_sym, gelf_getsymshndx);
+#if ELF_STRPTR_IS_BROKEN
+ ASSERT(!strcmp(
+ symname,
+ ((char *)elf_getdata(elf_getscn(lib->elf,
+ lib->symtab.shdr.sh_link),
+ NULL)->d_buf) +
+ lib_sym->st_name));
+#else
+ ASSERT(!strcmp(
+ symname,
+ elf_strptr(lib->elf, lib->symtab.shdr.sh_link,
+ lib_sym->st_name)));
+#endif
+ if (lib_sym->st_shndx != SHN_UNDEF &&
+ (GELF_ST_BIND(lib_sym->st_info) == STB_GLOBAL ||
+ GELF_ST_BIND(lib_sym->st_info) == STB_WEAK)) {
+ memcpy(lib_sym_mem, &sym_mem, sizeof(GElf_Sym));
+ return lib_sym;
+ }
+ }
+
+ return NULL;
+}
+
+static source_t *lookup_symbol_in_dependencies(source_t *source,
+ const char *symname,
+ GElf_Sym *found_sym)
+{
+ source_t *sym_source = NULL; /* return value */
+
+ /* This is an undefined symbol. Go over the list of libraries
+ and look it up. */
+ size_t libidx;
+ int found = 0;
+ source_t *last_found = NULL;
+ for (libidx = 0; libidx < (size_t)source->num_lib_deps; libidx++) {
+ source_t *lib = source->lib_deps[libidx];
+ if (hash_lookup_global_or_weak_symbol(lib, symname, found_sym) != NULL)
+ {
+ sym_source = lib;
+ if (found) {
+ if (found == 1) {
+ found++;
+ ERROR("ERROR: multiple definitions found for [%s:%s]!\n",
+ source->name, symname);
+ ERROR("\tthis definition [%s]\n", lib->name);
+ }
+ ERROR("\tprevious definition [%s]\n", last_found->name);
+ }
+ last_found = lib;
+ if (!found) found = 1;
+ }
+ }
+
+#if ELF_STRPTR_IS_BROKEN
+ ASSERT(!sym_source ||
+ !strcmp(symname,
+ (char *)(elf_getdata(elf_getscn(
+ sym_source->elf,
+ sym_source->symtab.shdr.sh_link),
+ NULL)->d_buf) +
+ found_sym->st_name));
+#else
+ ASSERT(!sym_source ||
+ !strcmp(symname,
+ elf_strptr(sym_source->elf,
+ sym_source->symtab.shdr.sh_link,
+ found_sym->st_name)));
+#endif
+
+ return sym_source;
+}
+
+static int do_prelink(source_t *source,
+ Elf_Data *reloc_scn_data,
+ int reloc_scn_entry_size,
+ unfinished_relocation_t *unfinished,
+ int locals_only,
+ bool dry_run,
+ char **lib_lookup_dirs, int num_lib_lookup_dirs,
+ char **default_libs, int num_default_libs,
+ int *num_unfinished_relocs)
+{
+ int num_relocations = 0;
+
+ size_t num_rels;
+ num_rels = reloc_scn_data->d_size / reloc_scn_entry_size;
+
+ INFO("\tThere are %d relocations.\n", num_rels);
+
+ int rel_idx;
+ for (rel_idx = 0; rel_idx < (size_t)num_rels; rel_idx++) {
+ GElf_Rel *rel, rel_mem;
+
+ //INFO("\tHandling relocation %d/%d\n", rel_idx, num_rels);
+
+ rel = gelf_getrel(reloc_scn_data, rel_idx, &rel_mem);
+ FAILIF_LIBELF(rel == NULL, gelf_getrel);
+ GElf_Sym *sym = NULL, sym_mem;
+ unsigned sym_idx = GELF_R_SYM(rel->r_info);
+ source_t *sym_source = NULL;
+ /* found_sym points to found_sym_mem, when sym_source != NULL, and
+ to sym, when the sybmol is locally defined. If the symbol is
+ not locally defined and sym_source == NULL, then sym is not
+ defined either. */
+ GElf_Sym *found_sym = NULL, found_sym_mem;
+ const char *symname = NULL;
+ int sym_is_local = 1;
+ if (sym_idx) {
+ sym = gelf_getsymshndx(source->symtab.data,
+ NULL,
+ sym_idx,
+ &sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+#if ELF_STRPTR_IS_BROKEN
+ symname =
+ ((char *)source->strtab.data->d_buf) +
+ sym->st_name;
+#else
+ symname = elf_strptr(source->elf,
+ elf_ndxscn(source->strtab.scn),
+ sym->st_name);
+#endif
+
+ /* If the symbol is defined and is either not in the BSS
+ section, or if it is in the BSS then the relocation is
+ not a copy relocation, then the symbol's source is this
+ library (i.e., it is locally-defined). Otherwise, the
+ symbol is imported.
+ */
+
+ sym_is_local = 0;
+ if (sym->st_shndx != SHN_UNDEF &&
+ (source->bss.scn == NULL ||
+ sym->st_shndx != elf_ndxscn(source->bss.scn) ||
+#ifdef ARM_SPECIFIC_HACKS
+ GELF_R_TYPE(rel->r_info) != R_ARM_COPY
+#else
+ 1
+#endif
+ ))
+ {
+ sym_is_local = 1;
+ }
+
+ if (sym_is_local) {
+ INFO("\t\tSymbol [%s:%s] is defined locally.\n",
+ source->name,
+ symname);
+ sym_source = source;
+ found_sym = sym;
+ }
+ else if (!locals_only) {
+ sym_source = lookup_symbol_in_dependencies(source,
+ symname,
+ &found_sym_mem);
+
+ /* The symbol was not in the list of dependencies, which by
+ itself is an error: it means either that the symbol does
+ not exist anywhere, or that the library which has the symbol
+ has not been listed as a dependency in this library or
+ executable. It could also mean (for a library) that the
+ symbol is defined in the executable that links agsinst it,
+ which is obviously not a good thing. These are bad things,
+ but they do happen, which is why we have the ability to
+ provide a list of default dependencies, including
+ executables. Here we check to see if the symbol has been
+ defined in any of them.
+ */
+ if (NULL == sym_source) {
+ INFO("\t\tChecking default dependencies...\n");
+ int i;
+ source_t *lib, *old_sym_source = NULL;
+ int printed_initial_error = 0;
+ for (i = 0; i < num_default_libs; i++) {
+ INFO("\tChecking in [%s].\n", default_libs[i]);
+ lib = find_source(default_libs[i],
+ lib_lookup_dirs,
+ num_lib_lookup_dirs);
+ FAILIF(NULL == lib,
+ "Can't find default library [%s]!\n",
+ default_libs[i]);
+ if (hash_lookup_global_or_weak_symbol(lib,
+ symname,
+ &found_sym_mem)) {
+ found_sym = &found_sym_mem;
+ sym_source = lib;
+#if ELF_STRPTR_IS_BROKEN
+ ASSERT(!strcmp(symname,
+ (char *)(elf_getdata(
+ elf_getscn(
+ sym_source->elf,
+ sym_source->symtab.
+ shdr.sh_link),
+ NULL)->d_buf) +
+ found_sym->st_name));
+#else
+ ASSERT(!strcmp(symname,
+ elf_strptr(sym_source->elf,
+ sym_source->symtab.shdr.sh_link,
+ found_sym->st_name)));
+
+#endif
+ INFO("\tFound symbol [%s] in [%s]!\n",
+ symname, lib->name);
+ if (old_sym_source) {
+ if (printed_initial_error == 0) {
+ printed_initial_error = 1;
+ ERROR("Multiple definition of [%s]:\n"
+ "\t[%s]\n",
+ symname,
+ old_sym_source->name);
+ }
+ ERROR("\t[%s]\n", sym_source->name);
+ }
+ old_sym_source = sym_source;
+ } else {
+ INFO("\tCould not find symbol [%s] in default "
+ "lib [%s]!\n", symname, lib->name);
+ }
+ }
+ if (sym_source) {
+ ERROR("ERROR: Could not find [%s:%s] in dependent "
+ "libraries (but found in default [%s])!\n",
+ source->name,
+ symname,
+ sym_source->name);
+ }
+ } else {
+ found_sym = &found_sym_mem;
+ /* We found the symbol in a dependency library. */
+ INFO("\t\tSymbol [%s:%s, value %lld] is imported from [%s]\n",
+ source->name,
+ symname,
+ found_sym->st_value,
+ sym_source->name);
+ }
+ } /* if symbol is defined in this library... */
+
+ if (!locals_only) {
+ /* If a symbol is weak and we haven't found it, then report
+ an error. We really need to find a way to set its value
+ to zero. The problem is that it needs to refer to some
+ section. */
+
+ FAILIF(NULL == sym_source &&
+ GELF_ST_BIND(sym->st_info) == STB_WEAK,
+ "Cannot handle weak symbols yet (%s:%s <- %s).\n",
+ source->name,
+ symname,
+ sym_source->name);
+#ifdef PERMISSIVE
+ if (GELF_ST_BIND(sym->st_info) != STB_WEAK &&
+ NULL == sym_source) {
+ ERROR("ERROR: Can't find symbol [%s:%s] in dependent or "
+ "default libraries!\n", source->name, symname);
+ }
+#else
+ FAILIF(GELF_ST_BIND(sym->st_info) != STB_WEAK &&
+ NULL == sym_source,
+ "Can't find symbol [%s:%s] in dependent or default "
+ "libraries!\n",
+ source->name,
+ symname);
+#endif
+ } /* if (!locals_only) */
+ }
+#if 0 // too chatty
+ else
+ INFO("\t\tno symbol is associated with this relocation\n");
+#endif
+
+
+ // We prelink only local symbols when locals_only == 1.
+
+ bool can_relocate = true;
+ if (!sym_is_local &&
+ (symname[0] == 'd' && symname[1] == 'l' && symname[2] != '\0' &&
+ (!strcmp(symname + 2, "open") ||
+ !strcmp(symname + 2, "close") ||
+ !strcmp(symname + 2, "sym") ||
+ !strcmp(symname + 2, "error")))) {
+ INFO("********* NOT RELOCATING LIBDL SYMBOL [%s]\n", symname);
+ can_relocate = false;
+ }
+
+ if (can_relocate && (sym_is_local || !locals_only))
+ {
+ GElf_Shdr shdr_mem; Elf_Scn *scn; Elf_Data *data;
+ find_section(source, rel->r_offset, &scn, &shdr_mem, &data);
+ unsigned *dest =
+ (unsigned*)(((char *)data->d_buf) +
+ (rel->r_offset - shdr_mem.sh_addr));
+ unsigned rel_type = GELF_R_TYPE(rel->r_info);
+ char buf[64];
+ INFO("\t\t%-15s ",
+ ebl_reloc_type_name(source->ebl,
+ GELF_R_TYPE(rel->r_info),
+ buf,
+ sizeof(buf)));
+
+ /* Section-name offsets do not change, so we use oldelf to get the
+ strings. This makes a difference in the second pass of the
+ perlinker, after the call to adjust_elf, because
+ source->shstrndx no longer contains the index of the
+ section-header-strings table.
+ */
+ const char *sname = elf_strptr(
+ source->oldelf, source->shstrndx, shdr_mem.sh_name);
+
+ switch (rel_type) {
+ case R_ARM_JUMP_SLOT:
+ case R_ARM_GLOB_DAT:
+ case R_ARM_ABS32:
+ ASSERT(data->d_buf != NULL);
+ ASSERT(data->d_size >= rel->r_offset - shdr_mem.sh_addr);
+#ifdef PERMISSIVE
+ if (sym_source == NULL) {
+ ERROR("ERROR: Permissive relocation "
+ "[%-15s] [%s:%s]: [0x%llx] = ZERO\n",
+ ebl_reloc_type_name(source->ebl,
+ GELF_R_TYPE(rel->r_info),
+ buf,
+ sizeof(buf)),
+ sname,
+ symname,
+ rel->r_offset);
+ if (!dry_run)
+ *dest = 0;
+ } else
+#endif
+ {
+ ASSERT(sym_source);
+ INFO("[%s:%s]: [0x%llx] = 0x%llx + 0x%lx\n",
+ sname,
+ symname,
+ rel->r_offset,
+ found_sym->st_value,
+ sym_source->base);
+ if (!dry_run)
+ *dest = found_sym->st_value + sym_source->base;
+ }
+ num_relocations++;
+ break;
+ case R_ARM_RELATIVE:
+ ASSERT(data->d_buf != NULL);
+ ASSERT(data->d_size >= rel->r_offset - shdr_mem.sh_addr);
+ FAILIF(sym != NULL,
+ "Unsupported RELATIVE form (symbol != 0)...\n");
+ INFO("[%s:%s]: [0x%llx] = 0x%x + 0x%lx\n",
+ sname,
+ symname ?: "(symbol has no name)",
+ rel->r_offset, *dest, source->base);
+ if (!dry_run)
+ *dest += source->base;
+ num_relocations++;
+ break;
+ case R_ARM_COPY:
+#ifdef PERMISSIVE
+ if (sym_source == NULL) {
+ ERROR("ERROR: Permissive relocation "
+ "[%-15s] [%s:%s]: NOT PERFORMING\n",
+ ebl_reloc_type_name(source->ebl,
+ GELF_R_TYPE(rel->r_info),
+ buf,
+ sizeof(buf)),
+ sname,
+ symname);
+ } else
+#endif
+ {
+ ASSERT(sym);
+ ASSERT(sym_source);
+ GElf_Shdr src_shdr_mem;
+ Elf_Scn *src_scn;
+ Elf_Data *src_data;
+ find_section(sym_source, found_sym->st_value,
+ &src_scn,
+ &src_shdr_mem,
+ &src_data);
+ INFO("Found [%s:%s (%lld)] in section [%s] .\n",
+ sym_source->name,
+ symname,
+ found_sym->st_value,
+#if ELF_STRPTR_IS_BROKEN
+ (((char *)elf_getdata(
+ elf_getscn(sym_source->elf,
+ sym_source->shstrndx),
+ NULL)->d_buf) + src_shdr_mem.sh_name)
+#else
+ elf_strptr(sym_source->elf,
+ sym_source->shstrndx,
+ src_shdr_mem.sh_name)
+#endif
+ );
+
+ unsigned *src = NULL;
+ if (src_data->d_buf == NULL)
+ {
+#ifdef PERMISSIVE
+ if (sym_source->bss.scn == NULL ||
+ elf_ndxscn(src_scn) !=
+ elf_ndxscn(sym_source->bss.scn)) {
+ ERROR("ERROR: Permissive relocation (NULL source "
+ "not from .bss) [%-15s] [%s:%s]: "
+ "NOT PERFORMING\n",
+ ebl_reloc_type_name(source->ebl,
+ GELF_R_TYPE(rel->r_info),
+ buf,
+ sizeof(buf)),
+ sname,
+ symname);
+ }
+#endif
+ }
+ else {
+ ASSERT(src_data->d_size >=
+ found_sym->st_value - src_shdr_mem.sh_addr);
+ src = (unsigned*)(((char *)src_data->d_buf) +
+ (found_sym->st_value -
+ src_shdr_mem.sh_addr));
+ }
+ ASSERT(symname);
+ INFO("[%s:%s]: [0x%llx] <- [0x%llx] size %lld\n",
+ sname,
+ symname, rel->r_offset,
+ found_sym->st_value,
+ found_sym->st_size);
+
+#ifdef PERMISSIVE
+ if (src_data->d_buf != NULL ||
+ (sym_source->bss.scn != NULL &&
+ elf_ndxscn(src_scn) ==
+ elf_ndxscn(sym_source->bss.scn)))
+#endif/*PERMISSIVE*/
+ {
+ if (data->d_buf == NULL) {
+ INFO("Incomplete relocation [%-15s] of [%s:%s].\n",
+ ebl_reloc_type_name(source->ebl,
+ GELF_R_TYPE(rel->r_info),
+ buf,
+ sizeof(buf)),
+ sname,
+ symname);
+ FAILIF(unfinished == NULL,
+ "You passed unfinished as NULL expecting "
+ "to handle all relocations, "
+ "but at least one cannot be handled!\n");
+ if (unfinished->num_rels == unfinished->rels_size) {
+ unfinished->rels_size += 10;
+ unfinished->rels = (GElf_Rel *)REALLOC(
+ unfinished->rels,
+ unfinished->rels_size *
+ sizeof(GElf_Rel));
+ }
+ unfinished->rels[unfinished->num_rels++] = *rel;
+ num_relocations--;
+ (*num_unfinished_relocs)++;
+ }
+ else {
+ if (src_data->d_buf != NULL)
+ {
+ ASSERT(data->d_buf != NULL);
+ ASSERT(data->d_size >= rel->r_offset -
+ shdr_mem.sh_addr);
+ if (!dry_run)
+ memcpy(dest, src, found_sym->st_size);
+ }
+ else {
+ ASSERT(src == NULL);
+ ASSERT(elf_ndxscn(src_scn) ==
+ elf_ndxscn(sym_source->bss.scn));
+ if (!dry_run)
+ memset(dest, 0, found_sym->st_size);
+ }
+ }
+ }
+ num_relocations++;
+ }
+ break;
+ default:
+ FAILIF(1, "Unknown relocation type %d!\n", rel_type);
+ } // switch
+ } // relocate
+ else {
+ INFO("\t\tNot relocating symbol [%s]%s\n",
+ symname,
+ (can_relocate ? ", relocating only locals" :
+ ", which is a libdl symbol"));
+ FAILIF(unfinished == NULL,
+ "You passed unfinished as NULL expecting to handle all "
+ "relocations, but at least one cannot be handled!\n");
+ if (unfinished->num_rels == unfinished->rels_size) {
+ unfinished->rels_size += 10;
+ unfinished->rels = (GElf_Rel *)REALLOC(
+ unfinished->rels,
+ unfinished->rels_size *
+ sizeof(GElf_Rel));
+ }
+ unfinished->rels[unfinished->num_rels++] = *rel;
+ (*num_unfinished_relocs)++;
+ }
+ } // for each relocation entry
+
+ return num_relocations;
+}
+
+static int prelink(source_t *source,
+ int locals_only,
+ bool dry_run,
+ char **lib_lookup_dirs, int num_lib_lookup_dirs,
+ char **default_libs, int num_default_libs,
+ int *num_unfinished_relocs)
+{
+ INFO("Prelinking [%s] (number of relocation sections: %d)%s...\n",
+ source->name, source->num_relocation_sections,
+ (dry_run ? " (dry run)" : ""));
+ int num_relocations = 0;
+ int rel_scn_idx;
+ for (rel_scn_idx = 0; rel_scn_idx < source->num_relocation_sections;
+ rel_scn_idx++)
+ {
+ section_info_t *reloc_scn = source->relocation_sections + rel_scn_idx;
+ unfinished_relocation_t *unfinished = source->unfinished + rel_scn_idx;
+
+ /* We haven't modified the shstrtab section, and so shdr->sh_name has
+ the same value as before. Thus we look up the name based on the old
+ ELF handle. We cannot use shstrndx on the new ELF handle because
+ the index of the shstrtab section may have changed (and calling
+ elf_getshstrndx() returns the same section index, so libelf can't
+ handle thise ither).
+
+ If reloc_scn->info is available, we can assert that the
+ section-name has not changed. If this assertion fails,
+ then we cannot use the elf_strptr() trick below to get
+ the section name. One solution would be to save it in
+ the section_info_t structure.
+ */
+ ASSERT(reloc_scn->info == NULL ||
+ reloc_scn->shdr.sh_name == reloc_scn->info->old_shdr.sh_name);
+ const char *sname =
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ reloc_scn->shdr.sh_name);
+ ASSERT(sname != NULL);
+
+ INFO("\n\tIterating relocation section [%s]...\n", sname);
+
+ /* In general, the new size of the section differs from the original
+ size of the section, because we can handle some of the relocations.
+ This was communicated to adjust_elf, which modified the ELF file
+ according to the new section sizes. Now, when prelink() does the
+ actual work of prelinking, it needs to know the original size of the
+ relocation section so that it can see all of the original relocation
+ entries!
+ */
+ size_t d_size = reloc_scn->data->d_size;
+ if (reloc_scn->info != NULL &&
+ reloc_scn->data->d_size != reloc_scn->info->old_shdr.sh_size)
+ {
+ INFO("Setting size of section [%s] to from new size %d to old "
+ "size %lld temporarily (so prelinker can see all "
+ "relocations).\n",
+ reloc_scn->info->name,
+ d_size,
+ reloc_scn->info->old_shdr.sh_size);
+ reloc_scn->data->d_size = reloc_scn->info->old_shdr.sh_size;
+ }
+
+ num_relocations +=
+ do_prelink(source,
+ reloc_scn->data, reloc_scn->shdr.sh_entsize,
+ unfinished,
+ locals_only, dry_run,
+ lib_lookup_dirs, num_lib_lookup_dirs,
+ default_libs, num_default_libs,
+ num_unfinished_relocs);
+
+ if (reloc_scn->data->d_size != d_size)
+ {
+ ASSERT(reloc_scn->info != NULL);
+ INFO("Resetting size of section [%s] to %d\n",
+ reloc_scn->info->name,
+ d_size);
+ reloc_scn->data->d_size = d_size;
+ }
+ }
+
+ /* Now prelink those relocation sections which were fully handled, and
+ therefore removed. They are not a part of the
+ source->relocation_sections[] array anymore, but we can find them by
+ scanning source->shdr_info[] and looking for sections with idx == 0.
+ */
+
+ if (ADJUST_ELF && source->shdr_info != NULL) {
+ /* Walk over the shdr_info[] array to see if we've removed any
+ relocation sections. prelink() those sections as well.
+ */
+ int i;
+ for (i = 0; i < source->shnum; i++) {
+ shdr_info_t *info = source->shdr_info + i;
+ if (info->idx == 0 &&
+ (info->shdr.sh_type == SHT_REL ||
+ info->shdr.sh_type == SHT_RELA)) {
+
+ Elf_Data *data = elf_getdata(info->scn, NULL);
+ ASSERT(data->d_size == 0);
+ data->d_size = info->old_shdr.sh_size;
+
+ INFO("\n\tIterating relocation section [%s], which was "
+ "discarded (size %d, entry size %lld).\n",
+ info->name,
+ data->d_size,
+ info->old_shdr.sh_entsize);
+
+ num_relocations +=
+ do_prelink(source,
+ data, info->old_shdr.sh_entsize,
+ NULL, /* the section was fully handled */
+ locals_only, dry_run,
+ lib_lookup_dirs, num_lib_lookup_dirs,
+ default_libs, num_default_libs,
+ num_unfinished_relocs);
+
+ data->d_size = 0;
+ }
+ }
+ }
+ return num_relocations;
+}
+
+static char * find_file(const char *libname,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs) {
+ if (libname[0] == '/') {
+ /* This is an absolute path name--just return it. */
+ /* INFO("ABSOLUTE PATH: [%s].\n", libname); */
+ return strdup(libname);
+ } else {
+ /* First try the working directory. */
+ int fd;
+ if ((fd = open(libname, O_RDONLY)) > 0) {
+ close(fd);
+ /* INFO("FOUND IN CURRENT DIR: [%s].\n", libname); */
+ return strdup(libname);
+ } else {
+ /* Iterate over all library paths. For each path, append the file
+ name and see if there is a file at that place. If that fails,
+ bail out. */
+
+ char *name;
+ while (num_lib_lookup_dirs--) {
+ size_t lib_len = strlen(*lib_lookup_dirs);
+ /* one extra character for the slash, and another for the
+ terminating NULL. */
+ name = (char *)MALLOC(lib_len + strlen(libname) + 2);
+ strcpy(name, *lib_lookup_dirs);
+ name[lib_len] = '/';
+ strcpy(name + lib_len + 1, libname);
+ if ((fd = open(name, O_RDONLY)) > 0) {
+ close(fd);
+ /* INFO("FOUND: [%s] in [%s].\n", libname, name); */
+ return name;
+ }
+ INFO("NOT FOUND: [%s] in [%s].\n", libname, name);
+ free(name);
+ }
+ }
+ }
+ return NULL;
+}
+
+static void adjust_dynamic_segment_entry_size(source_t *source,
+ dt_rel_info_t *dyn)
+{
+ /* Update the size entry in the DT_DYNAMIC segment. */
+ GElf_Dyn *dyn_entry, dyn_entry_mem;
+ dyn_entry = gelf_getdyn(source->dynamic.data,
+ dyn->sz_idx,
+ &dyn_entry_mem);
+ FAILIF_LIBELF(NULL == dyn_entry, gelf_getdyn);
+ /* If we are calling this function to adjust the size of the dynamic entry,
+ then there should be some unfinished relocations remaining. If there
+ are none, then we should remove the entry from the dynamic section
+ altogether.
+ */
+ ASSERT(dyn->num_unfinished_relocs);
+
+ size_t relsize = gelf_fsize(source->elf,
+ ELF_T_REL,
+ 1,
+ source->elf_hdr.e_version);
+
+ if (unlikely(verbose_flag)) {
+ char buf[64];
+ INFO("Updating entry %d: [%-10s], %08llx --> %08x\n",
+ dyn->sz_idx,
+ ebl_dynamic_tag_name (source->ebl, dyn_entry->d_tag,
+ buf, sizeof (buf)),
+ dyn_entry->d_un.d_val,
+ dyn->num_unfinished_relocs * relsize);
+ }
+
+ dyn_entry->d_un.d_val = dyn->num_unfinished_relocs * relsize;
+
+ FAILIF_LIBELF(!gelf_update_dyn(source->dynamic.data,
+ dyn->sz_idx,
+ dyn_entry),
+ gelf_update_dyn);
+}
+
+static void adjust_dynamic_segment_entries(source_t *source)
+{
+ /* This function many remove entries from the dynamic segment, but it won't
+ resize the relevant section. It'll just fill the remainted with empty
+ DT entries.
+
+ FIXME: This is not guaranteed right now. If a dynamic segment does not
+ end with null DT entries, I think this will break.
+ */
+ FAILIF(source->rel.processed,
+ "More than one section matches DT_REL entry in dynamic segment!\n");
+ FAILIF(source->jmprel.processed,
+ "More than one section matches DT_JMPREL entry in "
+ "dynamic segment!\n");
+ source->rel.processed =
+ source->jmprel.processed = 1;
+
+ if (source->rel.num_unfinished_relocs > 0)
+ adjust_dynamic_segment_entry_size(source, &source->rel);
+
+ if (source->jmprel.num_unfinished_relocs > 0)
+ adjust_dynamic_segment_entry_size(source, &source->jmprel);
+
+ /* If at least one of the entries is empty, then we need to remove it. We
+ have already adjusted the size of the other.
+ */
+ if (source->rel.num_unfinished_relocs == 0 ||
+ source->jmprel.num_unfinished_relocs == 0)
+ {
+ /* We need to delete the DT_REL/DT_RELSZ and DT_PLTREL/DT_PLTRELSZ
+ entries from the dynamic segment. */
+
+ GElf_Dyn *dyn_entry, dyn_entry_mem;
+ size_t dynidx, updateidx;
+
+ size_t numdyn =
+ source->dynamic.shdr.sh_size /
+ source->dynamic.shdr.sh_entsize;
+
+ for (updateidx = dynidx = 0; dynidx < numdyn; dynidx++)
+ {
+ dyn_entry = gelf_getdyn(source->dynamic.data,
+ dynidx,
+ &dyn_entry_mem);
+ FAILIF_LIBELF(NULL == dyn_entry, gelf_getdyn);
+ if ((source->rel.num_unfinished_relocs == 0 &&
+ (dynidx == source->rel.idx ||
+ dynidx == source->rel.sz_idx)) ||
+ (source->jmprel.num_unfinished_relocs == 0 &&
+ (dynidx == source->jmprel.idx ||
+ dynidx == source->jmprel.sz_idx)))
+ {
+ if (unlikely(verbose_flag)) {
+ char buf[64];
+ INFO("\t(!)\tRemoving entry %02d: [%-10s], %08llx\n",
+ dynidx,
+ ebl_dynamic_tag_name (source->ebl, dyn_entry->d_tag,
+ buf, sizeof (buf)),
+ dyn_entry->d_un.d_val);
+ }
+ continue;
+ }
+
+ if (unlikely(verbose_flag)) {
+ char buf[64];
+ INFO("\t\tKeeping entry %02d: [%-10s], %08llx\n",
+ dynidx,
+ ebl_dynamic_tag_name (source->ebl, dyn_entry->d_tag,
+ buf, sizeof (buf)),
+ dyn_entry->d_un.d_val);
+ }
+
+ gelf_update_dyn(source->dynamic.data,
+ updateidx,
+ &dyn_entry_mem);
+ updateidx++;
+ }
+ }
+} /* adjust_dynamic_segment_entries */
+
+static bool adjust_dynamic_segment_for(source_t *source,
+ dt_rel_info_t *dyn,
+ bool adjust_section_size_only)
+{
+ bool dropped_sections = false;
+
+ /* Go over the sections that belong to this dynamic range. */
+ dyn->num_unfinished_relocs = 0;
+ if (dyn->sections) {
+ int num_scns, idx;
+ range_t *scns = get_sorted_ranges(dyn->sections, &num_scns);
+
+ INFO("\tdynamic range %s:[%lld, %lld) contains %d sections.\n",
+ source->name,
+ dyn->addr,
+ dyn->addr + dyn->size,
+ num_scns);
+
+ ASSERT(scns);
+ int next_idx = 0, next_rel_off = 0;
+ /* The total number of unfinished relocations for this dynamic
+ * entry. */
+ section_info_t *next = (section_info_t *)scns[next_idx].user;
+ section_info_t *first = next;
+ ASSERT(first);
+ for (idx = 0; idx < num_scns; idx++) {
+ section_info_t *reloc_scn = (section_info_t *)scns[idx].user;
+ size_t rel_scn_idx = reloc_scn - source->relocation_sections;
+ ASSERT(rel_scn_idx < (size_t)source->num_relocation_sections);
+ unfinished_relocation_t *unfinished =
+ &source->unfinished[rel_scn_idx];
+ int unf_idx;
+
+ ASSERT(reloc_scn->info == NULL ||
+ reloc_scn->shdr.sh_name ==
+ reloc_scn->info->old_shdr.sh_name);
+ const char *sname =
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ reloc_scn->shdr.sh_name);
+
+ INFO("\tsection [%s] contains %d unfinished relocs.\n",
+ sname,
+ unfinished->num_rels);
+
+ for (unf_idx = 0; unf_idx < unfinished->num_rels; unf_idx++)
+ {
+ /* There are unfinished relocations. Copy them forward to the
+ lowest section we can. */
+
+ while (next_rel_off ==
+ (int)(next->shdr.sh_size/next->shdr.sh_entsize))
+ {
+ INFO("\tsection [%s] has filled up with %d unfinished "
+ "relocs.\n",
+ sname,
+ next_rel_off);
+
+ next_idx++;
+ ASSERT(next_idx <= idx);
+ next = (section_info_t *)scns[next_idx].user;
+ next_rel_off = 0;
+ }
+
+ if (!adjust_section_size_only) {
+ INFO("\t\tmoving unfinished relocation %2d to [%s:%d]\n",
+ unf_idx,
+ sname,
+ next_rel_off);
+ FAILIF_LIBELF(0 ==
+ gelf_update_rel(next->data,
+ next_rel_off,
+ &unfinished->rels[unf_idx]),
+ gelf_update_rel);
+ }
+
+ next_rel_off++;
+ dyn->num_unfinished_relocs++;
+ }
+ } /* for */
+
+ /* Set the size of the last section, and mark all subsequent
+ sections for removal. At this point, next is the section
+ to which we last wrote data, next_rel_off is the offset before
+ which we wrote the last relocation, and so next_rel_off *
+ relsize is the new size of the section.
+ */
+
+ bool adjust_file = ADJUST_ELF && source->elf_hdr.e_type != ET_EXEC;
+ if (adjust_file && !source->dry_run)
+ {
+ size_t relsize = gelf_fsize(source->elf,
+ ELF_T_REL,
+ 1,
+ source->elf_hdr.e_version);
+
+ ASSERT(next->info == NULL ||
+ next->shdr.sh_name == next->info->old_shdr.sh_name);
+ const char *sname =
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ next->shdr.sh_name);
+
+ INFO("\tsection [%s] (index %d) has %d unfinished relocs, "
+ "changing its size to %ld bytes (from %ld bytes).\n",
+ sname,
+ elf_ndxscn(next->scn),
+ next_rel_off,
+ (long)(next_rel_off * relsize),
+ (long)(next->shdr.sh_size));
+
+ /* source->shdr_info[] must be allocated prior to calling this
+ function. This is in fact done in process_file(), by calling
+ setup_shdr_info() just before we call adjust_dynamic_segment().
+ */
+ ASSERT(source->shdr_info != NULL);
+
+ /* We do not update the data field of shdr_info[], because it does
+ not exist yet (with ADJUST_ELF != 0). We create the new section
+ and section data after the first call to prelink(). For now, we
+ save the results of our analysis by modifying the sh_size field
+ of the section header. When we create the new sections' data,
+ we set the size of the data from the sh_size fields of the
+ section headers.
+
+ NOTE: The assertion applies only to the first call of
+ adjust_dynamic_segment (which calls this function). By
+ the second call, we've already created the data for the
+ new sections. The only sections for which we haven't
+ created data are the relocation sections we are removing.
+ */
+#ifdef DEBUG
+ ASSERT((!adjust_section_size_only &&
+ (source->shdr_info[elf_ndxscn(next->scn)].idx > 0)) ||
+ source->shdr_info[elf_ndxscn(next->scn)].data == NULL);
+#endif
+
+ //FIXME: what else do we need to do here? Do we need to update
+ // another copy of the shdr so that it's picked up when we
+ // commit the file?
+ next->shdr.sh_size = next_rel_off * relsize;
+ source->shdr_info[elf_ndxscn(next->scn)].shdr.sh_size =
+ next->shdr.sh_size;
+ if (next_rel_off * relsize == 0) {
+#ifdef REMOVE_HANDLED_SECTIONS
+ INFO("\tsection [%s] (index %d) is now empty, marking for "
+ "removal.\n",
+ sname,
+ elf_ndxscn(next->scn));
+ source->shdr_info[elf_ndxscn(next->scn)].idx = 0;
+ dropped_sections = true;
+#endif
+ }
+
+ while (++next_idx < num_scns) {
+ next = (section_info_t *)scns[next_idx].user;
+#ifdef REMOVE_HANDLED_SECTIONS
+ ASSERT(next->info == NULL ||
+ next->shdr.sh_name == next->info->old_shdr.sh_name);
+ const char *sname =
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ next->shdr.sh_name);
+ INFO("\tsection [%s] (index %d) is now empty, marking for "
+ "removal.\n",
+ sname,
+ elf_ndxscn(next->scn));
+ /* mark for removal */
+ source->shdr_info[elf_ndxscn(next->scn)].idx = 0;
+ dropped_sections = true;
+#endif
+ }
+ }
+
+ } /* if (dyn->sections) */
+ else {
+ /* The dynamic entry won't have any sections when it itself doesn't
+ exist. This could happen when we remove all relocation sections
+ from a dynamic entry because we have managed to handle all
+ relocations in them.
+ */
+ INFO("\tNo section for dynamic entry!\n");
+ }
+
+ return dropped_sections;
+}
+
+static bool adjust_dynamic_segment(source_t *source,
+ bool adjust_section_size_only)
+{
+ bool dropped_section;
+ INFO("Adjusting dynamic segment%s.\n",
+ (adjust_section_size_only ? " (section sizes only)" : ""));
+ INFO("\tadjusting dynamic segment REL.\n");
+ dropped_section =
+ adjust_dynamic_segment_for(source, &source->rel,
+ adjust_section_size_only);
+ INFO("\tadjusting dynamic segment JMPREL.\n");
+ dropped_section =
+ adjust_dynamic_segment_for(source, &source->jmprel,
+ adjust_section_size_only) ||
+ dropped_section;
+ if (!adjust_section_size_only)
+ adjust_dynamic_segment_entries(source);
+ return dropped_section;
+}
+
+static void match_relocation_sections_to_dynamic_ranges(source_t *source)
+{
+ /* We've gathered all the DT_DYNAMIC entries; now we need to figure out
+ which relocation sections fit in which range as described by the
+ entries.
+ */
+
+ int relidx;
+ for (relidx = 0; relidx < source->num_relocation_sections; relidx++) {
+ section_info_t *reloc_scn = &source->relocation_sections[relidx];
+
+ int index = elf_ndxscn(reloc_scn->scn);
+
+ ASSERT(reloc_scn->info == NULL ||
+ reloc_scn->shdr.sh_name == reloc_scn->info->old_shdr.sh_name);
+ const char *sname =
+ elf_strptr(source->oldelf,
+ source->shstrndx,
+ reloc_scn->shdr.sh_name);
+
+ INFO("Checking section [%s], index %d, for match to dynamic ranges\n",
+ sname, index);
+ if (source->shdr_info == NULL || reloc_scn->info->idx > 0) {
+ if (source->rel.addr &&
+ source->rel.addr <= reloc_scn->shdr.sh_addr &&
+ reloc_scn->shdr.sh_addr < source->rel.addr + source->rel.size)
+ {
+ /* The entire section must fit in the dynamic range. */
+ if((reloc_scn->shdr.sh_addr + reloc_scn->shdr.sh_size) >
+ (source->rel.addr + source->rel.size))
+ {
+ PRINT("WARNING: In [%s], section %s:[%lld,%lld) "
+ "is not fully contained in dynamic range "
+ "[%lld,%lld)!\n",
+ source->name,
+ sname,
+ reloc_scn->shdr.sh_addr,
+ reloc_scn->shdr.sh_addr +
+ reloc_scn->shdr.sh_size,
+ source->rel.addr,
+ source->rel.addr + source->rel.size);
+ }
+
+ if (NULL == source->rel.sections) {
+ source->rel.sections = init_range_list();
+ ASSERT(source->rel.sections);
+ }
+ add_unique_range_nosort(source->rel.sections,
+ reloc_scn->shdr.sh_addr,
+ reloc_scn->shdr.sh_size,
+ reloc_scn,
+ NULL,
+ NULL);
+ INFO("\tSection [%s] matches dynamic range REL.\n",
+ sname);
+ }
+ else if (source->jmprel.addr &&
+ source->jmprel.addr <= reloc_scn->shdr.sh_addr &&
+ reloc_scn->shdr.sh_addr <= source->jmprel.addr +
+ source->jmprel.size)
+ {
+ if((reloc_scn->shdr.sh_addr + reloc_scn->shdr.sh_size) >
+ (source->jmprel.addr + source->jmprel.size))
+ {
+ PRINT("WARNING: In [%s], section %s:[%lld,%lld) "
+ "is not fully "
+ "contained in dynamic range [%lld,%lld)!\n",
+ source->name,
+ sname,
+ reloc_scn->shdr.sh_addr,
+ reloc_scn->shdr.sh_addr +
+ reloc_scn->shdr.sh_size,
+ source->jmprel.addr,
+ source->jmprel.addr + source->jmprel.size);
+ }
+
+ if (NULL == source->jmprel.sections) {
+ source->jmprel.sections = init_range_list();
+ ASSERT(source->jmprel.sections);
+ }
+ add_unique_range_nosort(source->jmprel.sections,
+ reloc_scn->shdr.sh_addr,
+ reloc_scn->shdr.sh_size,
+ reloc_scn,
+ NULL,
+ NULL);
+ INFO("\tSection [%s] matches dynamic range JMPREL.\n",
+ sname);
+ }
+ else
+ PRINT("WARNING: Relocation section [%s:%s] does not match "
+ "any DT_ entry.\n",
+ source->name,
+ sname);
+ }
+ else {
+ INFO("Section [%s] was removed, not matching it to dynamic "
+ "ranges.\n",
+ sname);
+ }
+ } /* for ... */
+
+ if (source->rel.sections) sort_ranges(source->rel.sections);
+ if (source->jmprel.sections) sort_ranges(source->jmprel.sections);
+}
+
+static void drop_sections(source_t *source)
+{
+ INFO("We are dropping some sections from [%s]--creating section entries "
+ "only for remaining sections.\n",
+ source->name);
+ /* Renumber the sections. The numbers for the sections after those we are
+ dropping will be shifted back by the number of dropped sections. */
+ int cnt, idx;
+ for (cnt = idx = 1; cnt < source->shnum; ++cnt) {
+ if (source->shdr_info[cnt].idx > 0) {
+ source->shdr_info[cnt].idx = idx++;
+
+ /* Create a new section. */
+ FAILIF_LIBELF((source->shdr_info[cnt].newscn =
+ elf_newscn(source->elf)) == NULL, elf_newscn);
+ ASSERT(elf_ndxscn (source->shdr_info[cnt].newscn) ==
+ source->shdr_info[cnt].idx);
+
+ /* Copy the section data */
+ Elf_Data *olddata =
+ elf_getdata(source->shdr_info[cnt].scn, // old section
+ NULL);
+ FAILIF_LIBELF(NULL == olddata, elf_getdata);
+ Elf_Data *data =
+ elf_newdata(source->shdr_info[cnt].newscn);
+ FAILIF_LIBELF(NULL == data, elf_newdata);
+ *data = *olddata;
+#if COPY_SECTION_DATA_BUFFER
+ if (olddata->d_buf != NULL) {
+ data->d_buf = MALLOC(data->d_size);
+ memcpy(data->d_buf, olddata->d_buf, olddata->d_size);
+ }
+#endif
+ source->shdr_info[cnt].data = data;
+
+ if (data->d_size !=
+ source->shdr_info[cnt].shdr.sh_size) {
+ INFO("Trimming new-section data from %d to %lld bytes "
+ "(as calculated by adjust_dynamic_segment()).\n",
+ data->d_size,
+ source->shdr_info[cnt].shdr.sh_size);
+ data->d_size =
+ source->shdr_info[cnt].shdr.sh_size;
+ }
+
+ INFO("\tsection [%s] (old offset %lld, old size %lld) "
+ "will have index %d (was %d), new size %d\n",
+ source->shdr_info[cnt].name,
+ source->shdr_info[cnt].old_shdr.sh_offset,
+ source->shdr_info[cnt].old_shdr.sh_size,
+ source->shdr_info[cnt].idx,
+ elf_ndxscn(source->shdr_info[cnt].scn),
+ data->d_size);
+ } else {
+ INFO("\tIgnoring section [%s] (offset %lld, size %lld, index %d), "
+ "it will be discarded.\n",
+ source->shdr_info[cnt].name,
+ source->shdr_info[cnt].shdr.sh_offset,
+ source->shdr_info[cnt].shdr.sh_size,
+ elf_ndxscn(source->shdr_info[cnt].scn));
+ }
+
+ /* NOTE: We mark use_old_shdr_for_relocation_calculations even for the
+ sections we are removing. adjust_elf has an assertion that makes
+ sure that if the values for the size of a section according to its
+ header and its data structure differ, then we are using explicitly
+ the old section header for calculations, and that the section in
+ question is a relocation section.
+ */
+ source->shdr_info[cnt].use_old_shdr_for_relocation_calculations = true;
+ } /* for */
+}
+
+static source_t* process_file(const char *filename,
+ const char *output, int is_file,
+ void (*report_library_size_in_memory)(
+ const char *name, off_t fsize),
+ unsigned (*get_next_link_address)(
+ const char *name),
+ int locals_only,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs,
+ char **default_libs,
+ int num_default_libs,
+ int dry_run,
+ int *total_num_handled_relocs,
+ int *total_num_unhandled_relocs)
+{
+ /* Look up the file in the list of already-handles files, which are
+ represented by source_t structs. If we do not find the file, then we
+ haven't prelinked it yet. If we find it, then we have, so we do
+ nothing. Keep in mind that apriori operates on an entire collection
+ of files, and if application A used library L, and so does application
+ B, if we process A first, then by the time we get to B we will have
+ prelinked L already; that's why we check first to see if a library has
+ been prelinked.
+ */
+ source_t *source =
+ find_source(filename, lib_lookup_dirs, num_lib_lookup_dirs);
+ if (NULL == source) {
+ /* If we could not find the source, then it hasn't been processed yet,
+ so we go ahead and process it! */
+ INFO("Processing [%s].\n", filename);
+ char *full = find_file(filename, lib_lookup_dirs, num_lib_lookup_dirs);
+ FAILIF(NULL == full,
+ "Could not find [%s] in the current directory or in any of "
+ "the search paths!\n", filename);
+
+ unsigned base = get_next_link_address(full);
+
+ source = init_source(full, output, is_file, base, dry_run);
+
+ if (source == NULL) {
+ INFO("File [%s] is a static executable.\n", filename);
+ return NULL;
+ }
+ ASSERT(source->dynamic.scn != NULL);
+
+ /* We need to increment the next prelink address only when the file we
+ are currently handing is a shared library. Executables do not need
+ to be prelinked at a different address, they are always at address
+ zero.
+
+ Also, if we are prelinking locals only, then we are handling a
+ single file per invokation of apriori, so there is no need to
+ increment the prelink address unless there is a global prelink map,
+ in which case we do need to check to see if the library isn't
+ running into its neighbouts in the prelink map.
+ */
+ if (source->oldelf_hdr.e_type != ET_EXEC &&
+ (!locals_only ||
+ report_library_size_in_memory ==
+ pm_report_library_size_in_memory)) {
+ /* This sets the next link address only if an increment was not
+ specified by the user. If an address increment was specified,
+ then we just check to make sure that the file size is less than
+ the increment.
+
+ NOTE: The file size is the absolute highest number of bytes that
+ the file may occupy in memory, if the entire file is loaded, but
+ this is almost next the case. A file will often have sections
+ which are not loaded, which could add a lot of size. That's why
+ we start off with the file size and then subtract the size of
+ the biggest sections that will not get loaded, which are the
+ varios DWARF sections, all of which of which are named starting
+ with ".debug_".
+
+ We could do better than this (by caculating exactly how many
+ bytes from that file will be loaded), but that's an overkill.
+ Unless the prelink-address increment becomes too small, the file
+ size after subtracting the sizes of the DWARF section will be a
+ good-enough upper bound.
+ */
+
+ unsigned long fsize = source->elf_file_info.st_size;
+ INFO("Calculating loadable file size for next link address. "
+ "Starting with %ld.\n", fsize);
+ if (true) {
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr_mem, *shdr;
+ const char *scn_name;
+ while ((scn = elf_nextscn (source->oldelf, scn)) != NULL) {
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
+ scn_name = elf_strptr (source->oldelf,
+ source->shstrndx, shdr->sh_name);
+ ASSERT(scn_name != NULL);
+
+ if (!(shdr->sh_flags & SHF_ALLOC)) {
+ INFO("\tDecrementing by %lld on account of section "
+ "[%s].\n",
+ shdr->sh_size,
+ scn_name);
+ fsize -= shdr->sh_size;
+ }
+ }
+ }
+ INFO("Done calculating loadable file size for next link address: "
+ "Final value is %ld.\n", fsize);
+ report_library_size_in_memory(source->name, fsize);
+ }
+
+ /* Identify the dynamic segment and process it. Specifically, we find
+ out what dependencies, if any, this file has. Whenever we encounter
+ such a dependency, we process it recursively; we find out where the
+ various relocation information sections are stored. */
+
+ size_t dynidx;
+ GElf_Dyn *dyn, dyn_mem;
+ size_t numdyn =
+ source->dynamic.shdr.sh_size /
+ source->dynamic.shdr.sh_entsize;
+ ASSERT(source->dynamic.shdr.sh_size == source->dynamic.data->d_size);
+
+ source->rel.idx = source->rel.sz_idx = -1;
+ source->jmprel.idx = source->jmprel.sz_idx = -1;
+
+ for (dynidx = 0; dynidx < numdyn; dynidx++) {
+ dyn = gelf_getdyn (source->dynamic.data,
+ dynidx,
+ &dyn_mem);
+ FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
+ /* When we are processing only the local relocations in a file,
+ we don't need to handle any of the dependencies. It won't
+ hurt if we do, but we will be doing unnecessary work.
+ */
+ switch (dyn->d_tag)
+ {
+ case DT_NEEDED:
+ if (!locals_only) {
+ /* Process the needed library recursively.
+ */
+ const char *dep_lib =
+#if ELF_STRPTR_IS_BROKEN
+ (((char *)elf_getdata(
+ elf_getscn(source->elf,
+ source->dynamic.shdr.sh_link),
+ NULL)->d_buf) + dyn->d_un.d_val);
+#else
+ elf_strptr (source->elf,
+ source->dynamic.shdr.sh_link,
+ dyn->d_un.d_val);
+#endif
+ ASSERT(dep_lib != NULL);
+ INFO("[%s] depends on [%s].\n", filename, dep_lib);
+ ASSERT(output == NULL || is_file == 0);
+ source_t *dep = process_file(dep_lib,
+ output, is_file,
+ report_library_size_in_memory,
+ get_next_link_address,
+ locals_only,
+ lib_lookup_dirs,
+ num_lib_lookup_dirs,
+ default_libs,
+ num_default_libs,
+ dry_run,
+ total_num_handled_relocs,
+ total_num_unhandled_relocs);
+
+ /* Add the library to the dependency list. */
+ if (source->num_lib_deps == source->lib_deps_size) {
+ source->lib_deps_size += 10;
+ source->lib_deps = REALLOC(source->lib_deps,
+ source->lib_deps_size *
+ sizeof(source_t *));
+ }
+ source->lib_deps[source->num_lib_deps++] = dep;
+ }
+ break;
+ case DT_JMPREL:
+ source->jmprel.idx = dynidx;
+ source->jmprel.addr = dyn->d_un.d_ptr;
+ break;
+ case DT_PLTRELSZ:
+ source->jmprel.sz_idx = dynidx;
+ source->jmprel.size = dyn->d_un.d_val;
+ break;
+ case DT_REL:
+ source->rel.idx = dynidx;
+ source->rel.addr = dyn->d_un.d_ptr;
+ break;
+ case DT_RELSZ:
+ source->rel.sz_idx = dynidx;
+ source->rel.size = dyn->d_un.d_val;
+ break;
+ case DT_RELA:
+ case DT_RELASZ:
+ FAILIF(1, "Can't handle DT_RELA and DT_RELASZ entries!\n");
+ break;
+ } /* switch */
+ } /* for each dynamic entry... */
+
+ INFO("Handling [%s].\n", filename);
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ if (!source->prelinked)
+#endif
+ {
+ /* When ADJUST_ELF is defined, this call to prelink is a dry run
+ intended to calculate the number of relocations that could not
+ be handled. This, in turn, allows us to calculate the amount by
+ which we can shrink the various relocation sections before we
+ call adjust_elf. After we've adjusted the sections, we will
+ call prelink() one more time to do the actual work.
+
+ NOTE: Even when ADJUST_ELF != 0, we cannot adjust an ELF file
+ that is an executabe, because an executable is not PIC.
+ */
+
+ int num_unfinished_relocs = 0;
+ bool adjust_file = ADJUST_ELF && source->elf_hdr.e_type != ET_EXEC;
+ INFO("\n\n\tPRELINKING %s\n\n",
+ adjust_file ?
+ "(CALCULATE NUMBER OF HANDLED RELOCATIONS)" :
+ "(ACTUAL)");
+ int num_relocs = prelink(source, locals_only,
+ adjust_file || dry_run,
+ lib_lookup_dirs, num_lib_lookup_dirs,
+ default_libs, num_default_libs,
+ &num_unfinished_relocs);
+ INFO("[%s]: (calculate changes) handled %d, could not handle %d "
+ "relocations.\n",
+ source->name,
+ num_relocs,
+ num_unfinished_relocs);
+
+ if (adjust_file && !dry_run)
+ {
+ /* Find out the new section sizes of the relocation sections,
+ but do not move any relocations around, because adjust_elf
+ needs to know about all relocations in order to adjust the
+ file correctly.
+ */
+ match_relocation_sections_to_dynamic_ranges(source);
+
+ /* We haven't set up source->shdr_info[] yet, so we do it now.
+
+ NOTE: setup_shdr_info() depends only on source->oldelf, not
+ on source->elf! source->elf is not even defined yet. We
+ initialize source->shdr_info[] based on the section
+ information of the unmodified ELF file, and then make our
+ modifications in the call to adjust_dynamic_segment() based
+ on this information. adjust_dynamic_segment() will
+ rearrange the unhandled relocations in the beginning of
+ their relocation sections, and adjust the size of those
+ relocation sections. In the case when a relocation section
+ is completely handled, adjust_dynamic_segment() will mark it
+ for removal by function adjust_elf.
+ */
+
+ ASSERT(source->elf == source->oldelf);
+ ASSERT(source->shdr_info == NULL);
+ setup_shdr_info(source);
+ ASSERT(source->shdr_info != NULL);
+
+ INFO("\n\n\tADJUSTING DYNAMIC SEGMENT "
+ "(CALCULATE CHANGES)\n\n");
+ bool drop_some_sections = adjust_dynamic_segment(source, true);
+
+ /* Reopen the elf file! Note that we are not doing a dry run
+ (the if statement above makes sure of that.)
+
+ NOTE: We call init_elf() after we called
+ adjust_dynamic_segment() in order to have
+ adjust_dynamic_segment() refer to source->oldelf when
+ it refers to source->elf. Since
+ adjust_dynamic_segment doesn't actually write to the
+ ELF file, this is OK. adjust_dynamic_segment()
+ updates the sh_size fields of saved section headers
+ and optionally marks sections for removal.
+
+ Having adjust_dynamic_segment() refer to
+ source->oldelf means that we'll have access to
+ section-name strings so we can print them out in our
+ logging and debug output.
+ */
+ source->elf = init_elf(source, false);
+
+ /* This is the same code as in init_source() after the call to
+ * init_elf(). */
+ ASSERT(source->elf != source->oldelf);
+ ebl_closebackend(source->ebl);
+ source->ebl = ebl_openbackend (source->elf);
+ FAILIF_LIBELF(NULL == source->ebl, ebl_openbackend);
+#ifdef ARM_SPECIFIC_HACKS
+ FAILIF_LIBELF(0 != arm_init(source->elf,
+ source->elf_hdr.e_machine,
+ source->ebl, sizeof(Ebl)),
+ arm_init);
+#endif/*ARM_SPECIFIC_HACKS*/
+
+ if (drop_some_sections)
+ drop_sections(source);
+ else {
+ INFO("All sections remain in [%s]--we are changing at "
+ "most section sizes.\n", source->name);
+ create_elf_sections(source, NULL);
+ int cnt, idx;
+ for (cnt = idx = 1; cnt < source->shnum; ++cnt) {
+ Elf_Data *data = elf_getdata(
+ source->shdr_info[cnt].newscn, // new section
+ NULL);
+ if (data->d_size !=
+ source->shdr_info[cnt].shdr.sh_size) {
+ INFO("Trimming new-section data from %d to %lld "
+ "bytes (as calculated by "
+ "adjust_dynamic_segment()).\n",
+ data->d_size,
+ source->shdr_info[cnt].shdr.sh_size);
+ data->d_size = source->shdr_info[cnt].shdr.sh_size;
+ }
+ }
+ }
+
+ /* Shrink it! */
+ INFO("\n\n\tADJUSTING ELF\n\n");
+ adjust_elf(
+ source->oldelf, source->name,
+ source->elf, source->name,
+ source->ebl,
+ &source->old_ehdr_mem,
+ NULL, 0, // no symbol filter
+ source->shdr_info, // information on how to adjust the ELF
+ source->shnum, // length of source->shdr_info[]
+ source->phdr_info, // program-header info
+ source->shnum, // irrelevant--we're not rebuilding shstrtab
+ source->shnum, // number of sections in file
+ source->shstrndx, // index of shstrtab (both in
+ // shdr_info[] and as a section index)
+ NULL, // irrelevant, since we are not rebuilding shstrtab
+ drop_some_sections, // some sections are being dropped
+ elf_ndxscn(source->dynamic.scn), // index of .dynamic
+ elf_ndxscn(source->symtab.scn), // index of .dynsym
+ 1, // allow shady business
+ &source->shstrtab_data,
+ true,
+ false); // do not rebuild shstrtab
+
+ INFO("\n\n\tREINITIALIZING STRUCTURES "
+ "(TO CONTAIN ADJUSTMENTS)\n\n");
+ reinit_source(source);
+
+ INFO("\n\n\tPRELINKING (ACTUAL)\n\n");
+#ifdef DEBUG
+ int old_num_unfinished_relocs = num_unfinished_relocs;
+#endif
+ num_unfinished_relocs = 0;
+#ifdef DEBUG
+ int num_relocs_take_two =
+#endif
+ prelink(source, locals_only,
+ false, /* not a dry run */
+ lib_lookup_dirs, num_lib_lookup_dirs,
+ default_libs, num_default_libs,
+ &num_unfinished_relocs);
+
+ /* The numbers for the total number of relocations and the
+ number of unhandled relocations between the first and second
+ invokationof prelink() must be the same! The first time we
+ ran prelink() just to calculate the numbers so that we could
+ calculate the adjustments to pass to adjust_elf, and the
+ second time we actually carry out the prelinking; the
+ numbers must stay the same!
+ */
+ ASSERT(num_relocs == num_relocs_take_two);
+ ASSERT(old_num_unfinished_relocs == num_unfinished_relocs);
+
+ INFO("[%s]: (actual prelink) handled %d, could not "
+ "handle %d relocations.\n",
+ source->name,
+ num_relocs,
+ num_unfinished_relocs);
+ } /* if (adjust_elf && !dry_run) */
+
+ *total_num_handled_relocs += num_relocs;
+ *total_num_unhandled_relocs += num_unfinished_relocs;
+
+ if(num_unfinished_relocs != 0 &&
+ source->elf_hdr.e_type != ET_EXEC &&
+ !locals_only)
+ {
+ /* One reason you could have unfinished relocations in an
+ executable file is if this file used dlopen() and friends.
+ We do not adjust relocation entries to those symbols,
+ because libdl is a dummy only--the real functions are
+ provided for by the dynamic linker itsef.
+
+ NOTE FIXME HACK: This is specific to the Android dynamic
+ linker, and may not be true in other cases.
+ */
+ PRINT("WARNING: Expecting to have unhandled relocations only "
+ "for executables (%s is not an executable)!\n",
+ source->name);
+ }
+
+ match_relocation_sections_to_dynamic_ranges(source);
+
+ /* Now, for each relocation section, check to see if its address
+ matches one of the DT_DYNAMIC relocation pointers. If so, then
+ if the section has no unhandled relocations, simply set the
+ associated DT_DYNAMIC entry's size to zero. If the section does
+ have unhandled entries, then lump them all together at the front
+ of the respective section and update the size of the respective
+ DT_DYNAMIC entry to the new size of the section. A better
+ approach would be do delete a relocation section if it has been
+ fully relocated and to remove its entry from the DT_DYNAMIC
+ array, and for relocation entries that still have some
+ relocations in them, we should shrink the section if that won't
+ violate relative offsets. This is more work, however, and for
+ the speed improvement we expect from a prelinker, just patching
+ up DT_DYNAMIC will suffice.
+
+ Note: adjust_dynamic_segment() will modify source->shdr_info[]
+ to denote any change in a relocation section's size. This
+ will be picked up by adjust_elf, which will rearrange the
+ file to eliminate the gap created by the decrease in size
+ of the relocation section. We do not need to do this, but
+ the relocation section could be large, and reduced
+ drastically by the prelinking process, so it pays to
+ adjust the file.
+ */
+
+ INFO("\n\n\tADJUSTING DYNAMIC SEGMENT (ACTUAL)\n\n");
+ adjust_dynamic_segment(source, false);
+ }
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ else INFO("[%s] is already prelinked at 0x%08lx.\n",
+ filename,
+ source->prelink_base);
+#endif
+ } else INFO("[%s] has been processed already.\n", filename);
+
+ return source;
+}
+
+void apriori(char **execs, int num_execs,
+ char *output,
+ void (*report_library_size_in_memory)(
+ const char *name, off_t fsize),
+ int (*get_next_link_address)(const char *name),
+ int locals_only,
+ int dry_run,
+ char **lib_lookup_dirs, int num_lib_lookup_dirs,
+ char **default_libs, int num_default_libs,
+ char *mapfile)
+{
+ source_t *source; /* for general usage */
+ int input_idx;
+
+ ASSERT(report_library_size_in_memory != NULL);
+ ASSERT(get_next_link_address != NULL);
+
+ /* Process and prelink each executable and object file. Function
+ process_file() is called for each executable in the loop below.
+ It calls itself recursively for each library. We prelink each library
+ after prelinking its dependencies. */
+ int total_num_handled_relocs = 0, total_num_unhandled_relocs = 0;
+ for (input_idx = 0; input_idx < num_execs; input_idx++) {
+ INFO("executable: [%s]\n", execs[input_idx]);
+ /* Here process_file() is actually processing the top-level
+ executable files. */
+ process_file(execs[input_idx], output, num_execs == 1,
+ report_library_size_in_memory,
+ get_next_link_address, /* executables get a link address
+ of zero, regardless of this
+ value */
+ locals_only,
+ lib_lookup_dirs, num_lib_lookup_dirs,
+ default_libs, num_default_libs,
+ dry_run,
+ &total_num_handled_relocs,
+ &total_num_unhandled_relocs);
+ /* if source is NULL, then the respective executable is static */
+ /* Mark the source as an executable */
+ } /* for each input executable... */
+
+ PRINT("Handled %d relocations.\n", total_num_handled_relocs);
+ PRINT("Could not handle %d relocations.\n", total_num_unhandled_relocs);
+
+ /* We are done! Since the end result of our calculations is a set of
+ symbols for each library that other libraries or executables link
+ against, we iterate over the set of libraries one last time, and for
+ each symbol that is marked as satisfying some dependence, we emit
+ a line with the symbol's name to a text file derived from the library's
+ name by appending the suffix .syms to it. */
+
+ if (mapfile != NULL) {
+ const char *mapfile_name = mapfile;
+ FILE *fp;
+ if (*mapfile == '+') {
+ mapfile_name = mapfile + 1;
+ INFO("Opening map file %s for append/write.\n",
+ mapfile_name);
+ fp = fopen(mapfile_name, "a");
+ }
+ else fp = fopen(mapfile_name, "w");
+
+ FAILIF(fp == NULL, "Cannot open file [%s]: %s (%d)!\n",
+ mapfile_name,
+ strerror(errno),
+ errno);
+ source = sources;
+ while (source) {
+ /* If it's a library, print the results. */
+ if (source->elf_hdr.e_type == ET_DYN) {
+ /* Add to the memory map file. */
+ fprintf(fp, "%s 0x%08lx %lld\n",
+ basename(source->name),
+ source->base,
+ source->elf_file_info.st_size);
+ }
+ source = source->next;
+ }
+ fclose(fp);
+ }
+
+ /* Free the resources--you can't do it in the loop above because function
+ print_symbol_references() accesses nodes other than the one being
+ iterated over.
+ */
+ source = sources;
+ while (source) {
+ source_t *old = source;
+ source = source->next;
+ /* Destroy the evidence. */
+ destroy_source(old);
+ }
+}
diff --git a/tools/apriori/apriori.h b/tools/apriori/apriori.h
new file mode 100644
index 000000000..5e396fdfc
--- /dev/null
+++ b/tools/apriori/apriori.h
@@ -0,0 +1,14 @@
+#ifndef LSD_H
+#define LSD_H
+
+void apriori(char **execs, int num_execs,
+ char *output,
+ void (*set_next_link_address)(const char *name, off_t fsize),
+ int (*get_next_link_address)(const char *name),
+ int locals_only,
+ int dry_run,
+ char **lib_lookup_dirs, int num_lib_lookup_dirs,
+ char **default_libs, int num_default_libs,
+ char *mapfile);
+
+#endif
diff --git a/tools/apriori/cmdline.c b/tools/apriori/cmdline.c
new file mode 100644
index 000000000..95f112a88
--- /dev/null
+++ b/tools/apriori/cmdline.c
@@ -0,0 +1,186 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] = {
+ {"start-address", required_argument, 0, 's'},
+ {"inc-address", required_argument, 0, 'i'},
+ {"locals-only", no_argument, 0, 'l'},
+ {"quiet", no_argument, 0, 'Q'},
+ {"noupdate", no_argument, 0, 'n'},
+ {"lookup", required_argument, 0, 'L'},
+ {"default", required_argument, 0, 'D'},
+ {"verbose", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {"mapfile", required_argument, 0, 'M'},
+ {"output", required_argument, 0, 'o'},
+ {"prelinkmap", required_argument, 0, 'p'},
+ {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static const char *descriptions[] = {
+ "start address to prelink libraries to",
+ "address increment for each library",
+ "prelink local relocations only",
+ "suppress informational and non-fatal error messages",
+ "do a dry run--calculate the prelink info but do not update any files",
+ "provide a directory for library lookup",
+ "provide a default library or executable for symbol lookup",
+ "print verbose output",
+ "print help screen",
+ "print a list of prelink addresses to file (prefix filename with + to append instead of overwrite)",
+ "specify an output directory (if multiple inputs) or file (is single input)",
+ "specify a file with prelink addresses instead of a --start-address/--inc-address combination",
+};
+
+void print_help(const char *name) {
+ fprintf(stdout,
+ "invokation:\n"
+ "\t%s file1 [file2 file3 ...] -Ldir1 [-Ldir2 ...] -saddr -iinc [-Vqn] [-M<logfile>]\n"
+ "\t%s -l file [-Vqn] [-M<logfile>]\n"
+ "\t%s -h\n\n", name, name, name);
+ fprintf(stdout, "options:\n");
+ struct option *opt = long_options;
+ const char **desc = descriptions;
+ while (opt->name) {
+ fprintf(stdout, "\t-%c/--%s%s: %s\n",
+ opt->val,
+ opt->name,
+ (opt->has_arg ? " (argument)" : ""),
+ *desc);
+ opt++;
+ desc++;
+ }
+}
+
+int get_options(int argc, char **argv,
+ int *start_addr,
+ int *inc_addr,
+ int *locals_only,
+ int *quiet,
+ int *dry_run,
+ char ***dirs,
+ int *num_dirs,
+ char ***defaults,
+ int *num_defaults,
+ int *verbose,
+ char **mapfile,
+ char **output,
+ char **prelinkmap) {
+ int c;
+
+ ASSERT(dry_run); *dry_run = 0;
+ ASSERT(quiet); *quiet = 0;
+ ASSERT(verbose); *verbose = 0;
+ ASSERT(dirs); *dirs = NULL;
+ ASSERT(num_dirs); *num_dirs = 0;
+ ASSERT(defaults); *defaults = NULL;
+ ASSERT(num_defaults); *num_defaults = 0;
+ ASSERT(start_addr); *start_addr = -1;
+ ASSERT(inc_addr); *inc_addr = -1;
+ ASSERT(locals_only); *locals_only = 0;
+ ASSERT(mapfile); *mapfile = NULL;
+ ASSERT(output); *output = NULL;
+ ASSERT(prelinkmap); *prelinkmap = NULL;
+ int dirs_size = 0;
+ int defaults_size = 0;
+
+ while (1) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv,
+ "VhnQlL:D:s:i:M:o:p:",
+ long_options,
+ &option_index);
+ /* Detect the end of the options. */
+ if (c == -1) break;
+
+ if (isgraph(c)) {
+ INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+ }
+
+#define SET_STRING_OPTION(name) do { \
+ ASSERT(optarg); \
+ (*name) = strdup(optarg); \
+} while(0)
+
+#define SET_REPEATED_STRING_OPTION(arr, num, size) do { \
+ if (*num == size) { \
+ size += 10; \
+ *arr = (char **)REALLOC(*arr, size * sizeof(char *)); \
+ } \
+ SET_STRING_OPTION(((*arr) + *num)); \
+ (*num)++; \
+} while(0)
+
+#define SET_INT_OPTION(val) do { \
+ ASSERT(optarg); \
+ if (strlen(optarg) >= 2 && optarg[0] == '0' && optarg[1] == 'x') { \
+ FAILIF(1 != sscanf(optarg+2, "%x", val), \
+ "Expecting a hexadecimal argument!\n"); \
+ } else { \
+ FAILIF(1 != sscanf(optarg, "%d", val), \
+ "Expecting a decimal argument!\n"); \
+ } \
+} while(0)
+
+ switch (c) {
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ if (long_options[option_index].flag != 0)
+ break;
+ INFO ("option %s", long_options[option_index].name);
+ if (optarg)
+ INFO (" with arg %s", optarg);
+ INFO ("\n");
+ break;
+ case 'Q': *quiet = 1; break;
+ case 'n': *dry_run = 1; break;
+ case 'M':
+ SET_STRING_OPTION(mapfile);
+ break;
+ case 'o':
+ SET_STRING_OPTION(output);
+ break;
+ case 'p':
+ SET_STRING_OPTION(prelinkmap);
+ break;
+ case 's':
+ SET_INT_OPTION(start_addr);
+ break;
+ case 'i':
+ SET_INT_OPTION(inc_addr);
+ break;
+ case 'L':
+ SET_REPEATED_STRING_OPTION(dirs, num_dirs, dirs_size);
+ break;
+ case 'D':
+ SET_REPEATED_STRING_OPTION(defaults, num_defaults, defaults_size);
+ break;
+ case 'l': *locals_only = 1; break;
+ case 'h': print_help(argv[0]); exit(1); break;
+ case 'V': *verbose = 1; break;
+ case '?':
+ /* getopt_long already printed an error message. */
+ break;
+
+#undef SET_STRING_OPTION
+#undef SET_REPEATED_STRING_OPTION
+#undef SET_INT_OPTION
+
+ default:
+ FAILIF(1, "Unknown option");
+ }
+ }
+
+ return optind;
+}
diff --git a/tools/apriori/cmdline.h b/tools/apriori/cmdline.h
new file mode 100644
index 000000000..8f7f394be
--- /dev/null
+++ b/tools/apriori/cmdline.h
@@ -0,0 +1,21 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(const char *executable_name);
+
+int get_options(int argc, char **argv,
+ int *start_addr,
+ int *addr_increment,
+ int *locals_only,
+ int *quiet,
+ int *dry_run,
+ char ***dirs,
+ int *num_dirs,
+ char ***defaults,
+ int *num_defaults,
+ int *verbose,
+ char **mapfile,
+ char **output,
+ char **prelinkmap);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/apriori/common.h b/tools/apriori/common.h
new file mode 100644
index 000000000..f5d9d2ef8
--- /dev/null
+++ b/tools/apriori/common.h
@@ -0,0 +1,28 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+static inline int is_host_little(void)
+{
+ short val = 0x10;
+ return ((char *)&val)[0] != 0;
+}
+
+static inline long switch_endianness(long val)
+{
+ long newval;
+ ((char *)&newval)[3] = ((char *)&val)[0];
+ ((char *)&newval)[2] = ((char *)&val)[1];
+ ((char *)&newval)[1] = ((char *)&val)[2];
+ ((char *)&newval)[0] = ((char *)&val)[3];
+ return newval;
+}
+
+#endif/*COMMON_H*/
diff --git a/tools/apriori/debug.c b/tools/apriori/debug.c
new file mode 100644
index 000000000..263e09f32
--- /dev/null
+++ b/tools/apriori/debug.c
@@ -0,0 +1,38 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ fprintf(s, "%p: ", b);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ } else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n%p: ", i, b);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
+
diff --git a/tools/apriori/debug.h b/tools/apriori/debug.h
new file mode 100644
index 000000000..39968984b
--- /dev/null
+++ b/tools/apriori/debug.h
@@ -0,0 +1,88 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR PRINT
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/apriori/hash.c b/tools/apriori/hash.c
new file mode 100644
index 000000000..9f1a61426
--- /dev/null
+++ b/tools/apriori/hash.c
@@ -0,0 +1,27 @@
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <hash.h>
+#include <string.h>
+
+int hash_lookup(Elf *elf,
+ Elf_Data *hash,
+ Elf_Data *symtab,
+ Elf_Data *symstr,
+ const char *symname) {
+ Elf32_Word *hash_data = (Elf32_Word *)hash->d_buf;
+ Elf32_Word index;
+ Elf32_Word nbuckets = *hash_data++;
+ Elf32_Word *buckets = ++hash_data;
+ Elf32_Word *chains = hash_data + nbuckets;
+
+ index = buckets[elf_hash(symname) % nbuckets];
+ while (index != STN_UNDEF &&
+ strcmp((char *)symstr->d_buf +
+ ((Elf32_Sym *)symtab->d_buf)[index].st_name,
+ symname)) {
+ index = chains[index];
+ }
+
+ return index;
+}
diff --git a/tools/apriori/hash.h b/tools/apriori/hash.h
new file mode 100644
index 000000000..af29b9e04
--- /dev/null
+++ b/tools/apriori/hash.h
@@ -0,0 +1,14 @@
+#ifndef HASH_H
+#define HASH_H
+
+#include <common.h>
+#include <libelf.h>
+#include <gelf.h>
+
+int hash_lookup(Elf *elf,
+ Elf_Data *hash,
+ Elf_Data *symtab,
+ Elf_Data *symstr,
+ const char *symname);
+
+#endif/*HASH_H*/
diff --git a/tools/apriori/main.c b/tools/apriori/main.c
new file mode 100644
index 000000000..552392a1c
--- /dev/null
+++ b/tools/apriori/main.c
@@ -0,0 +1,229 @@
+/* TODO:
+ 1. check the ARM EABI version--this works for versions 1 and 2.
+ 2. use a more-intelligent approach to finding the symbol table,
+ symbol-string table, and the .dynamic section.
+ 3. fix the determination of the host and ELF-file endianness
+ 4. write the help screen
+*/
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <elf.h>
+#include <gelf.h>
+#include <cmdline.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <apriori.h>
+#include <prelinkmap.h>
+
+/* Flag set by --verbose. This variable is global as it is accessed by the
+ macro INFO() in multiple compilation unites. */
+int verbose_flag = 0;
+/* Flag set by --quiet. This variable is global as it is accessed by the
+ macro PRINT() in multiple compilation unites. */
+int quiet_flag = 0;
+static void print_dynamic_symbols(Elf *elf, const char *symtab_name);
+
+static unsigned s_next_link_addr;
+static off_t s_addr_increment;
+
+static void report_library_size_in_memory(const char *name, off_t fsize)
+{
+ ASSERT(s_next_link_addr != -1UL);
+ INFO("Setting next link address (current is at 0x%08x):\n",
+ s_next_link_addr);
+ if (s_addr_increment) {
+ FAILIF(s_addr_increment < fsize,
+ "Command-line-specified address increment of 0x%08llx (%lld) "
+ "less than file [%s]'s size of %lld bytes!\n",
+ s_addr_increment, s_addr_increment, name, fsize);
+ FAILIF(s_next_link_addr % 4096,
+ "User-provided address increment 0x%08lx "
+ "is not page-aligned!\n",
+ s_addr_increment);
+ INFO("\tignoring file size, adjusting by address increment.\n");
+ s_next_link_addr += s_addr_increment;
+ }
+ else {
+ INFO("\tuser address increment is zero, adjusting by file size.\n");
+ s_next_link_addr += fsize;
+ s_next_link_addr &= ~(4096 - 1);
+ }
+ INFO("\t[%s] file size 0x%08lx\n",
+ name,
+ fsize);
+ INFO("\tnext prelink address: 0x%08x\n", s_next_link_addr);
+ ASSERT(!(s_next_link_addr % 4096)); /* New address must be page-aligned */
+}
+
+static unsigned get_next_link_address(const char *name) {
+ return s_next_link_addr;
+}
+
+int main(int argc, char **argv) {
+ /* Do not issue INFO() statements before you call get_options() to set
+ the verbose flag as necessary.
+ */
+
+ char **lookup_dirs, **default_libs;
+ char *mapfile, *output, *prelinkmap;
+ int start_addr, inc_addr, locals_only, num_lookup_dirs,
+ num_default_libs, dry_run;
+ int first = get_options(argc, argv,
+ &start_addr, &inc_addr, &locals_only,
+ &quiet_flag,
+ &dry_run,
+ &lookup_dirs, &num_lookup_dirs,
+ &default_libs, &num_default_libs,
+ &verbose_flag,
+ &mapfile,
+ &output,
+ &prelinkmap);
+
+ /* Perform some command-line-parameter checks. */
+ int cmdline_err = 0;
+ if (first == argc) {
+ ERROR("You must specify at least one input ELF file!\n");
+ cmdline_err++;
+ }
+ /* We complain when the user does not specify a start address for
+ prelinking when the user does not pass the locals_only switch. The
+ reason is that we will have a collection of executables, which we always
+ prelink to zero, and shared libraries, which we prelink at the specified
+ prelink address. When the user passes the locals_only switch, we do not
+ fail if the user does not specify start_addr, because the file to
+ prelink may be an executable, and not a shared library. At this moment,
+ we do not know what the case is. We find that out when we call function
+ init_source().
+ */
+ if (!locals_only && start_addr == -1) {
+ ERROR("You must specify --start-addr!\n");
+ cmdline_err++;
+ }
+ if (start_addr == -1 && inc_addr != -1) {
+ ERROR("You must provide a start address if you provide an "
+ "address increment!\n");
+ cmdline_err++;
+ }
+ if (prelinkmap != NULL && start_addr != -1) {
+ ERROR("You may not provide a prelink-map file (-p) and use -s/-i "
+ "at the same time!\n");
+ cmdline_err++;
+ }
+ if (inc_addr == 0) {
+ ERROR("You may not specify a link-address increment of zero!\n");
+ cmdline_err++;
+ }
+ if (locals_only) {
+ if (argc - first == 1) {
+ if (inc_addr != -1) {
+ ERROR("You are prelinking a single file; there is no point in "
+ "specifying a prelink-address increment!\n");
+ /* This is nonfatal error, but paranoia is healthy. */
+ cmdline_err++;
+ }
+ }
+ if (lookup_dirs != NULL || default_libs != NULL) {
+ ERROR("You are prelinking local relocations only; there is "
+ "no point in specifying lookup directories!\n");
+ /* This is nonfatal error, but paranoia is healthy. */
+ cmdline_err++;
+ }
+ }
+
+ /* If there is an output option, then that must specify a file, if there is
+ a single input file, or a directory, if there are multiple input
+ files. */
+ if (output != NULL) {
+ struct stat output_st;
+ FAILIF(stat(output, &output_st) < 0 && errno != ENOENT,
+ "stat(%s): %s (%d)\n",
+ output,
+ strerror(errno),
+ errno);
+
+ if (argc - first == 1) {
+ FAILIF(!errno && !S_ISREG(output_st.st_mode),
+ "you have a single input file: -o must specify a "
+ "file name!\n");
+ }
+ else {
+ FAILIF(errno == ENOENT,
+ "you have multiple input files: -o must specify a "
+ "directory name, but %s does not exist!\n",
+ output);
+ FAILIF(!S_ISDIR(output_st.st_mode),
+ "you have multiple input files: -o must specify a "
+ "directory name, but %s is not a directory!\n",
+ output);
+ }
+ }
+
+ if (cmdline_err) {
+ print_help(argv[0]);
+ FAILIF(1, "There are command-line-option errors.\n");
+ }
+
+ /* Check to see whether the ELF library is current. */
+ FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
+
+ if (inc_addr < 0) {
+ if (!locals_only)
+ PRINT("User has not provided an increment address, "
+ "will use library size to calculate successive "
+ "prelink addresses.\n");
+ inc_addr = 0;
+ }
+
+ void (*func_report_library_size_in_memory)(const char *name, off_t fsize);
+ unsigned (*func_get_next_link_address)(const char *name);
+
+ if (prelinkmap != NULL) {
+ INFO("Reading prelink addresses from prelink-map file [%s].\n",
+ prelinkmap);
+ pm_init(prelinkmap);
+ func_report_library_size_in_memory = pm_report_library_size_in_memory;
+ func_get_next_link_address = pm_get_next_link_address;
+ }
+ else {
+ INFO("Start address: 0x%x\n", start_addr);
+ INFO("Increment address: 0x%x\n", inc_addr);
+ s_next_link_addr = start_addr;
+ s_addr_increment = inc_addr;
+ func_report_library_size_in_memory = report_library_size_in_memory;
+ func_get_next_link_address = get_next_link_address;
+ }
+
+ /* Prelink... */
+ apriori(&argv[first], argc - first, output,
+ func_report_library_size_in_memory, func_get_next_link_address,
+ locals_only,
+ dry_run,
+ lookup_dirs, num_lookup_dirs,
+ default_libs, num_default_libs,
+ mapfile);
+
+ FREEIF(mapfile);
+ FREEIF(output);
+ if (lookup_dirs) {
+ ASSERT(num_lookup_dirs);
+ while (num_lookup_dirs--)
+ FREE(lookup_dirs[num_lookup_dirs]);
+ FREE(lookup_dirs);
+ }
+ if (default_libs) {
+ ASSERT(num_default_libs);
+ while (num_default_libs--)
+ FREE(default_libs[num_default_libs]);
+ FREE(default_libs);
+ }
+
+ return 0;
+}
diff --git a/tools/apriori/prelink_info.c b/tools/apriori/prelink_info.c
new file mode 100644
index 000000000..da7ca057c
--- /dev/null
+++ b/tools/apriori/prelink_info.c
@@ -0,0 +1,106 @@
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <prelink_info.h>
+#include <debug.h>
+#include <common.h>
+
+typedef struct {
+ int32_t mmap_addr;
+ char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t __attribute__((packed));
+
+static inline void set_prelink(long *prelink_addr,
+ int elf_little,
+ prelink_info_t *info)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ if (prelink_addr) {
+ if (!(elf_little ^ is_host_little())) {
+ /* Same endianness */
+ *prelink_addr = info->mmap_addr;
+ }
+ else {
+ /* Different endianness */
+ *prelink_addr = switch_endianness(info->mmap_addr);
+ }
+ }
+}
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ int fd = open(fname, O_RDONLY);
+ FAILIF(fd < 0, "open(%s, O_RDONLY): %s (%d)!\n",
+ fname, strerror(errno), errno);
+ off_t end = lseek(fd, 0, SEEK_END);
+
+ int nr = sizeof(prelink_info_t);
+
+ off_t sz = lseek(fd, -nr, SEEK_CUR);
+ ASSERT((long)(end - sz) == (long)nr);
+ FAILIF(sz == (off_t)-1,
+ "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+ fd, strerror(errno), errno);
+
+ prelink_info_t info;
+ int num_read = read(fd, &info, nr);
+ FAILIF(num_read < 0,
+ "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+ fd, strerror(errno), errno);
+ FAILIF(num_read != sizeof(info),
+ "read(%d, &info, sizeof(prelink_info_t)): did not read %d bytes as "
+ "expected (read %d)!\n",
+ fd, sizeof(info), num_read);
+
+ int prelinked = 0;
+ if (!strncmp(info.tag, "PRE ", 4)) {
+ set_prelink(prelink_addr, elf_little, &info);
+ prelinked = 1;
+ }
+ FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+ return prelinked;
+}
+
+void setup_prelink_info(const char *fname, int elf_little, long base)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ int fd = open(fname, O_WRONLY);
+ FAILIF(fd < 0,
+ "open(%s, O_WRONLY): %s (%d)\n" ,
+ fname, strerror(errno), errno);
+ prelink_info_t info;
+ off_t sz = lseek(fd, 0, SEEK_END);
+ FAILIF(sz == (off_t)-1,
+ "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+ fd, strerror(errno), errno);
+
+ if (!(elf_little ^ is_host_little())) {
+ /* Same endianness */
+ INFO("Host and ELF file [%s] have same endianness.\n", fname);
+ info.mmap_addr = base;
+ }
+ else {
+ /* Different endianness */
+ INFO("Host and ELF file [%s] have different endianness.\n", fname);
+ info.mmap_addr = switch_endianness(base);
+ }
+ strncpy(info.tag, "PRE ", 4);
+
+ int num_written = write(fd, &info, sizeof(info));
+ FAILIF(num_written < 0,
+ "write(%d, &info, sizeof(info)): %s (%d)\n",
+ fd, strerror(errno), errno);
+ FAILIF(sizeof(info) != num_written,
+ "Could not write %d bytes (wrote only %d bytes) as expected!\n",
+ sizeof(info), num_written);
+ FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+}
+
+#endif /*SUPPORT_ANDROID_PRELINK_TAGS*/
diff --git a/tools/apriori/prelink_info.h b/tools/apriori/prelink_info.h
new file mode 100644
index 000000000..e2787cb04
--- /dev/null
+++ b/tools/apriori/prelink_info.h
@@ -0,0 +1,9 @@
+#ifndef PRELINK_INFO_H
+#define PRELINK_INFO_H
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr);
+void setup_prelink_info(const char *fname, int elf_little, long base);
+
+#endif
+#endif/*PRELINK_INFO_H*/
diff --git a/tools/apriori/prelinkmap.c b/tools/apriori/prelinkmap.c
new file mode 100644
index 000000000..739c18176
--- /dev/null
+++ b/tools/apriori/prelinkmap.c
@@ -0,0 +1,139 @@
+#include <prelinkmap.h>
+#include <debug.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <ctype.h>
+
+typedef struct mapentry mapentry;
+
+struct mapentry
+{
+ mapentry *next;
+ unsigned base;
+ char name[0];
+};
+
+static mapentry *maplist = 0;
+
+/* These values limit the address range within which we prelinked libraries
+ reside. The limit is not set in stone, but should be observed in the
+ prelink map, or the prelink step will fail.
+*/
+
+#define PRELINK_MIN 0x90000000
+#define PRELINK_MAX 0xB0000000
+
+void pm_init(const char *file)
+{
+ unsigned line = 0;
+ char buf[256];
+ char *x;
+ unsigned n;
+ FILE *fp;
+ mapentry *me;
+ unsigned last = -1UL;
+
+ fp = fopen(file, "r");
+ FAILIF(fp == NULL, "Error opening file %s: %s (%d)\n",
+ file, strerror(errno), errno);
+
+ while(fgets(buf, 256, fp)){
+ x = buf;
+ line++;
+
+ /* eat leading whitespace */
+ while(isspace(*x)) x++;
+
+ /* comment or blank line? skip! */
+ if(*x == '#') continue;
+ if(*x == 0) continue;
+
+ /* skip name */
+ while(*x && !isspace(*x)) x++;
+
+ if(*x) {
+ *x++ = 0;
+ /* skip space before address */
+ while(*x && isspace(*x)) x++;
+ }
+
+ /* no address? complain. */
+ if(*x == 0) {
+ fprintf(stderr,"warning: %s:%d no base address specified\n",
+ file, line);
+ continue;
+ }
+
+ n = strtoul(x, 0, 16);
+ /* Note that this is not the only bounds check. If a library's size
+ exceeds its slot as defined in the prelink map, the prelinker will
+ exit with an error. See pm_report_library_size_in_memory().
+ */
+ FAILIF((n < PRELINK_MIN) || (n > PRELINK_MAX),
+ "%s:%d base 0x%08x out of range.\n",
+ file, line, n);
+
+ me = malloc(sizeof(mapentry) + strlen(buf) + 1);
+ FAILIF(me == NULL, "Out of memory parsing %s\n", file);
+
+ FAILIF(last <= n, "The prelink map is not in descending order "
+ "at entry %s (%08x)!\n", buf, n);
+ last = n;
+
+ me->base = n;
+ strcpy(me->name, buf);
+ me->next = maplist;
+ maplist = me;
+ }
+
+ fclose(fp);
+}
+
+/* apriori() calls this function when it determine the size of a library
+ in memory. pm_report_library_size_in_memory() makes sure that the library
+ fits in the slot provided by the prelink map.
+*/
+void pm_report_library_size_in_memory(const char *name,
+ off_t fsize)
+{
+ char *x;
+ mapentry *me;
+
+ x = strrchr(name,'/');
+ if(x) name = x+1;
+
+ for(me = maplist; me; me = me->next){
+ if(!strcmp(name, me->name)) {
+ off_t slot = me->next ? me->next->base : PRELINK_MAX;
+ slot -= me->base;
+ FAILIF(fsize > slot,
+ "prelink map error: library %s@0x%08x is too big "
+ "at %lld bytes, it runs %lld bytes into "
+ "library %s@0x%08x!\n",
+ me->name, me->base, fsize, fsize - slot,
+ me->next->name, me->next->base);
+ break;
+ }
+ }
+
+ FAILIF(!me,"library '%s' not in prelink map\n", name);
+}
+
+unsigned pm_get_next_link_address(const char *lookup_name)
+{
+ char *x;
+ mapentry *me;
+
+ x = strrchr(lookup_name,'/');
+ if(x) lookup_name = x+1;
+
+ for(me = maplist; me; me = me->next){
+ if(!strcmp(lookup_name, me->name)) {
+ return me->base;
+ }
+ }
+
+ FAILIF(1==1,"library '%s' not in prelink map\n", lookup_name);
+ return 0;
+}
diff --git a/tools/apriori/prelinkmap.h b/tools/apriori/prelinkmap.h
new file mode 100644
index 000000000..17f766014
--- /dev/null
+++ b/tools/apriori/prelinkmap.h
@@ -0,0 +1,10 @@
+#ifndef PRELINKMAP_H
+#define PRELINKMAP_H
+
+#include <sys/types.h>
+
+extern void pm_init(const char *file);
+extern void pm_report_library_size_in_memory(const char *name, off_t fsize);
+extern unsigned pm_get_next_link_address(const char *name);
+
+#endif/*PRELINKMAP_H*/
diff --git a/tools/apriori/rangesort.c b/tools/apriori/rangesort.c
new file mode 100644
index 000000000..b0295e81c
--- /dev/null
+++ b/tools/apriori/rangesort.c
@@ -0,0 +1,317 @@
+#include <common.h>
+#include <debug.h>
+#include <rangesort.h>
+
+#define PARALLEL_ARRAY_SIZE (5)
+
+struct range_list_t {
+ range_t *array;
+#ifdef DEBUG
+ int is_sorted;
+#endif
+ int array_length;
+ int num_ranges;
+};
+
+range_list_t* init_range_list(void) {
+ range_list_t *ranges = (range_list_t *)MALLOC(sizeof(range_list_t));
+
+ ranges->array = (range_t *)MALLOC(PARALLEL_ARRAY_SIZE*sizeof(range_t));
+ ranges->array_length = PARALLEL_ARRAY_SIZE;
+ ranges->num_ranges = 0;
+#ifdef DEBUG
+ ranges->is_sorted = 0;
+#endif
+ return ranges;
+}
+
+void destroy_range_list(range_list_t *ranges) {
+ int idx;
+ for (idx = 0; idx < ranges->num_ranges; idx++) {
+ if (ranges->array[idx].user_dtor) {
+ ASSERT(ranges->array[idx].user);
+ ranges->array[idx].user_dtor(ranges->array[idx].user);
+ }
+ }
+ FREE(ranges->array);
+ FREE(ranges);
+}
+
+static inline int CONTAINS(range_t *container, range_t *contained) {
+ return container->start <= contained->start && contained->length &&
+ (container->start + container->length >
+ contained->start + contained->length);
+}
+
+static inline int IN_RANGE(range_t *range, GElf_Off point) {
+ return
+ range->start <= point &&
+ point < (range->start + range->length);
+}
+
+static inline int INTERSECT(range_t *left, range_t *right) {
+ return
+ (IN_RANGE(left, right->start) &&
+ IN_RANGE(right, left->start + left->length)) ||
+ (IN_RANGE(right, left->start) &&
+ IN_RANGE(left, right->start + right->length));
+}
+
+static int range_cmp_for_search(const void *l, const void *r) {
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+ if (INTERSECT(left, right) ||
+ CONTAINS(left, right) ||
+ CONTAINS(right, left)) {
+ return 0;
+ }
+ return left->start - right->start;
+}
+
+static inline void run_checks(const void *l, const void *r) {
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+ if (CONTAINS(left, right)) {
+ if (left->err_fn)
+ left->err_fn(ERROR_CONTAINS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
+ left->start, left->start + left->length,
+ right->start, right->start + right->length);
+ }
+ if (CONTAINS(right, left)) {
+ if (right->err_fn)
+ right->err_fn(ERROR_CONTAINS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld) contains [%lld, %lld)!\n",
+ right->start, right->start + right->length,
+ left->start, left->start + left->length);
+ }
+ if (INTERSECT(left, right)) {
+ if (left->err_fn)
+ left->err_fn(ERROR_OVERLAPS, left, right);
+ FAILIF(1, "Range sorting error: [%lld, %lld)and [%lld, %lld) intersect!\n",
+ left->start, left->start + left->length,
+ right->start, right->start + right->length);
+ }
+}
+
+static int range_cmp(const void *l, const void *r) {
+ run_checks(l, r);
+ range_t *left = (range_t *)l, *right = (range_t *)r;
+ return left->start - right->start;
+}
+
+void add_unique_range_nosort(
+ range_list_t *ranges,
+ GElf_Off start,
+ GElf_Off length,
+ void *user,
+ void (*err_fn)(range_error_t, range_t *, range_t *),
+ void (*user_dtor)(void * ))
+{
+ if (ranges->num_ranges == ranges->array_length) {
+ ranges->array_length += PARALLEL_ARRAY_SIZE;
+ ranges->array = REALLOC(ranges->array,
+ ranges->array_length*sizeof(range_t));
+ }
+ ranges->array[ranges->num_ranges].start = start;
+ ranges->array[ranges->num_ranges].length = length;
+ ranges->array[ranges->num_ranges].user = user;
+ ranges->array[ranges->num_ranges].err_fn = err_fn;
+ ranges->array[ranges->num_ranges].user_dtor = user_dtor;
+ ranges->num_ranges++;
+}
+
+range_list_t *sort_ranges(range_list_t *ranges) {
+ if (ranges->num_ranges > 1)
+ qsort(ranges->array, ranges->num_ranges, sizeof(range_t), range_cmp);
+ ranges->is_sorted = 1;
+ return ranges;
+}
+
+range_t *find_range(range_list_t *ranges, GElf_Off value) {
+#if 1
+ int i;
+ for (i = 0; i < ranges->num_ranges; i++) {
+ if (ranges->array[i].start <= value &&
+ value < ranges->array[i].start + ranges->array[i].length)
+ return ranges->array + i;
+ }
+ return NULL;
+#else
+ ASSERT(ranges->is_sorted); /* The range list must be sorted */
+ range_t lookup;
+ lookup.start = value;
+ lookup.length = 0;
+ return
+ (range_t *)bsearch(&lookup,
+ ranges->array, ranges->num_ranges, sizeof(range_t),
+ range_cmp_for_search);
+#endif
+}
+
+int get_num_ranges(const range_list_t *ranges)
+{
+ return ranges->num_ranges;
+}
+
+range_t *get_sorted_ranges(const range_list_t *ranges, int *num_ranges) {
+ ASSERT(ranges->is_sorted); /* The range list must be sorted */
+ if (num_ranges) {
+ *num_ranges = ranges->num_ranges;
+ }
+ return ranges->array;
+}
+
+GElf_Off get_last_address(const range_list_t *ranges) {
+ ASSERT(ranges->num_ranges);
+ return
+ ranges->array[ranges->num_ranges-1].start +
+ ranges->array[ranges->num_ranges-1].length;
+}
+
+static void handle_range_error(range_error_t err,
+ range_t *left, range_t *right) {
+ switch (err) {
+ case ERROR_CONTAINS:
+ ERROR("ERROR: section (%lld, %lld bytes) contains "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ case ERROR_OVERLAPS:
+ ERROR("ERROR: Section (%lld, %lld bytes) intersects "
+ "section (%lld, %lld bytes)\n",
+ left->start, left->length,
+ right->start, right->length);
+ break;
+ default:
+ ASSERT(!"Unknown range error code!");
+ }
+
+ FAILIF(1, "Range error.\n");
+}
+
+static void destroy_contiguous_range_info(void *user) {
+ contiguous_range_info_t *info = (contiguous_range_info_t *)user;
+ FREE(info->ranges);
+ FREE(info);
+}
+
+static void handle_contiguous_range_error(range_error_t err,
+ range_t *left,
+ range_t *right)
+{
+ contiguous_range_info_t *left_data =
+ (contiguous_range_info_t *)left->user;
+ ASSERT(left_data);
+ contiguous_range_info_t *right_data =
+ (contiguous_range_info_t *)right->user;
+ ASSERT(right_data);
+
+ PRINT("Contiguous-range overlap error. Printing contained ranges:\n");
+ int cnt;
+ PRINT("\tLeft ranges:\n");
+ for (cnt = 0; cnt < left_data->num_ranges; cnt++) {
+ PRINT("\t\t[%lld, %lld)\n",
+ left_data->ranges[cnt].start,
+ left_data->ranges[cnt].start + left_data->ranges[cnt].length);
+ }
+ PRINT("\tRight ranges:\n");
+ for (cnt = 0; cnt < right_data->num_ranges; cnt++) {
+ PRINT("\t\t[%lld, %lld)\n",
+ right_data->ranges[cnt].start,
+ right_data->ranges[cnt].start + right_data->ranges[cnt].length);
+ }
+
+ handle_range_error(err, left, right);
+}
+
+range_list_t* get_contiguous_ranges(const range_list_t *input)
+{
+ ASSERT(input);
+ FAILIF(!input->is_sorted,
+ "get_contiguous_ranges(): input range list is not sorted!\n");
+
+ range_list_t* ret = init_range_list();
+ int num_ranges;
+ range_t *ranges = get_sorted_ranges(input, &num_ranges);
+
+ int end_idx = 0;
+ while (end_idx < num_ranges) {
+ int start_idx = end_idx++;
+ int old_end_idx = start_idx;
+ int total_length = ranges[start_idx].length;
+ while (end_idx < num_ranges) {
+ if (ranges[old_end_idx].start + ranges[old_end_idx].length !=
+ ranges[end_idx].start)
+ break;
+ old_end_idx = end_idx++;
+ total_length += ranges[old_end_idx].length;
+ }
+
+ contiguous_range_info_t *user =
+ (contiguous_range_info_t *)MALLOC(sizeof(contiguous_range_info_t));
+ user->num_ranges = end_idx - start_idx;
+ user->ranges = (range_t *)MALLOC(user->num_ranges * sizeof(range_t));
+ int i;
+ for (i = 0; i < end_idx - start_idx; i++)
+ user->ranges[i] = ranges[start_idx + i];
+ add_unique_range_nosort(ret,
+ ranges[start_idx].start,
+ total_length,
+ user,
+ handle_contiguous_range_error,
+ destroy_contiguous_range_info);
+ }
+
+ return ret;
+}
+
+range_list_t* subtract_ranges(const range_list_t *r, const range_list_t *s)
+{
+ ASSERT(r); ASSERT(r->is_sorted);
+ ASSERT(s); ASSERT(s->is_sorted);
+
+ range_list_t *result = init_range_list();
+
+ int r_num_ranges, r_idx;
+ range_t *r_ranges = get_sorted_ranges(r, &r_num_ranges);
+ ASSERT(r_ranges);
+
+ int s_num_ranges, s_idx;
+ range_t *s_ranges = get_sorted_ranges(s, &s_num_ranges);
+ ASSERT(s_ranges);
+
+ s_idx = 0;
+ for (r_idx = 0; r_idx < r_num_ranges; r_idx++) {
+ GElf_Off last_start = r_ranges[r_idx].start;
+ for (; s_idx < s_num_ranges; s_idx++) {
+ if (CONTAINS(&r_ranges[r_idx], &s_ranges[s_idx])) {
+ if (last_start ==
+ r_ranges[r_idx].start + r_ranges[r_idx].length) {
+ break;
+ }
+ if (last_start == s_ranges[s_idx].start) {
+ last_start += s_ranges[s_idx].length;
+ continue;
+ }
+ INFO("Adding subtracted range [%lld, %lld)\n",
+ last_start,
+ s_ranges[s_idx].start);
+ add_unique_range_nosort(
+ result,
+ last_start,
+ s_ranges[s_idx].start - last_start,
+ NULL,
+ NULL,
+ NULL);
+ last_start = s_ranges[s_idx].start + s_ranges[s_idx].length;
+ } else {
+ ASSERT(!INTERSECT(&r_ranges[r_idx], &s_ranges[s_idx]));
+ break;
+ }
+ } /* while (s_idx < s_num_ranges) */
+ } /* for (r_idx = 0; r_idx < r_num_ranges; r_idx++) */
+
+ return result;
+}
+
+
diff --git a/tools/apriori/rangesort.h b/tools/apriori/rangesort.h
new file mode 100644
index 000000000..21db35706
--- /dev/null
+++ b/tools/apriori/rangesort.h
@@ -0,0 +1,105 @@
+#ifndef RANGESORT_H
+#define RANGESORT_H
+
+/* This implements a simple sorted list of non-overlapping ranges. */
+
+#include <debug.h>
+#include <common.h>
+#include <gelf.h>
+
+typedef enum range_error_t {
+ ERROR_CONTAINS,
+ ERROR_OVERLAPS
+} range_error_t;
+
+typedef struct range_t range_t;
+struct range_t {
+ GElf_Off start;
+ GElf_Off length;
+ void *user;
+ void (*err_fn)(range_error_t, range_t *, range_t *);
+ void (*user_dtor)(void *);
+};
+
+typedef struct range_list_t range_list_t;
+
+range_list_t* init_range_list();
+void destroy_range_list(range_list_t *);
+
+/* Just adds a range to the list. We won't detect whether the range overlaps
+ other ranges or contains them, or is contained by them, till we call
+ sort_ranges(). */
+void add_unique_range_nosort(range_list_t *ranges,
+ GElf_Off start, GElf_Off length,
+ void *user,
+ void (*err_fn)(range_error_t, range_t *, range_t *),
+ void (*user_dtor)(void * ));
+
+/* Sorts the ranges. If there are overlapping ranges or ranges that contain
+ other ranges, it will cause the program to exit with a FAIL. */
+range_list_t* sort_ranges(range_list_t *ranges);
+/* Find which range value falls in. Return that range or NULL if value does
+ not fall within any range. */
+range_t *find_range(range_list_t *ranges, GElf_Off value);
+int get_num_ranges(const range_list_t *ranges);
+range_t *get_sorted_ranges(const range_list_t *ranges, int *num_ranges);
+GElf_Off get_last_address(const range_list_t *ranges);
+
+/* This returns a range_list_t handle that contains ranges composed of the
+ adjacent ranges of the input range list. The user data of each range in
+ the range list is a structure of the type contiguous_range_info_t.
+ This structure contains an array of pointers to copies of the original
+ range_t structures comprising each new contiguous range, as well as the
+ length of that array.
+
+ NOTE: The input range must be sorted!
+
+ NOTE: destroy_range_list() will take care of releasing the data that it
+ allocates as a result of calling get_contiguous_ranges(). Do not free that
+ data yourself.
+
+ NOTE: the user data of the original range_t structures is simply copied, so
+ be careful handling it. You can destroy the range_list_t with
+ destroy_range_list() as usual. On error, the function does not return--the
+ program terminates.
+
+ NOTE: The returned range is not sorted. You must call sort_ranges() if you
+ need to.
+*/
+
+typedef struct {
+ int num_ranges;
+ range_t *ranges;
+} contiguous_range_info_t;
+
+range_list_t* get_contiguous_ranges(const range_list_t *);
+
+/* The function below takes in two range lists: r and s, and subtracts the
+ ranges in s from those in r. For example, if r and s are as follows:
+
+ r = { [0, 10) }
+ s = { [3, 5), [7, 9) }
+
+ Then r - s is { [0, 3), [5, 7), [9, 10) }
+
+ NOTE: Both range lists must be sorted on input. This is guarded by an
+ assertion.
+
+ NOTE: Range s must contain ranges, which are fully contained by the span of
+ range r (the span being the interval between the start of the lowest
+ range in r, inclusive, and the end of the highest range in r,
+ exclusive).
+
+ NOTE: In addition to the requirement above, range s must contain ranges,
+ each of which is a subrange of one of the ranges of r.
+
+ NOTE: There is no user info associated with the resulting range.
+
+ NOTE: The resulting range is not sorted.
+
+ Ther returned list must be destroyed with destroy_range_list().
+*/
+
+range_list_t* subtract_ranges(const range_list_t *r, const range_list_t *s);
+
+#endif/*RANGESORT_H*/
diff --git a/tools/apriori/source.c b/tools/apriori/source.c
new file mode 100644
index 000000000..69c57c774
--- /dev/null
+++ b/tools/apriori/source.c
@@ -0,0 +1,18 @@
+#include <source.h>
+
+void find_section(source_t *source, Elf64_Addr address,
+ Elf_Scn **scn,
+ GElf_Shdr *shdr,
+ Elf_Data **data)
+{
+ range_t *range = find_range(source->sorted_sections, address);
+ FAILIF(NULL == range,
+ "Cannot match address %lld to any range in [%s]!\n",
+ address,
+ source->name);
+ *scn = (Elf_Scn *)range->user;
+ ASSERT(*scn);
+ FAILIF_LIBELF(NULL == gelf_getshdr(*scn, shdr), gelf_getshdr);
+ *data = elf_getdata(*scn, NULL);
+ FAILIF_LIBELF(NULL == *data, elf_getdata);
+}
diff --git a/tools/apriori/source.h b/tools/apriori/source.h
new file mode 100644
index 000000000..a5d96bd2f
--- /dev/null
+++ b/tools/apriori/source.h
@@ -0,0 +1,121 @@
+#ifndef SOURCE_H
+#define SOURCE_H
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libelf.h>
+#include <libebl.h>
+#ifdef ARM_SPECIFIC_HACKS
+ #include <libebl_arm.h>
+#endif/*ARM_SPECIFIC_HACKS*/
+#include <elf.h>
+#include <gelf.h>
+#include <rangesort.h>
+#include <elfcopy.h>
+
+typedef struct source_t source_t;
+
+typedef struct {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+ shdr_info_t *info;
+} section_info_t;
+
+typedef struct {
+ GElf_Rel *rels;
+ int num_rels; /* number of relocations that were not finished */
+ int rels_size; /* this is the size of rels[], NOT the number of rels! */
+} unfinished_relocation_t;
+
+typedef struct {
+ int processed;
+ size_t idx; /* index of DT entry in the .dynamic section, if entry has a ptr value */
+ Elf64_Addr addr; /* if DT entry's value is an address, we save it here */
+ size_t sz_idx; /* index of DT entry in the .dynamic section, if entry has a size value */
+ Elf64_Xword size; /* if DT entry's value is a size, we save it here */
+
+ range_list_t *sections; /* list of sections corresponding to this entry */
+ int num_unfinished_relocs; /* this variables is populated by adjust_dynamic_segment_for()
+ during the second pass of the prelinker */
+} dt_rel_info_t;
+
+struct source_t {
+ source_t *next;
+
+ char *name; /* full path name of this executable file */
+ char *output; /* name of the output file or directory */
+ int output_is_dir; /* nonzero if output is a directory, 0 if output is a file */
+ /* ELF-related information: */
+ Elf *oldelf;
+ Elf *elf;
+ /* info[] is an array of structures describing the sections of the new ELF
+ file. We populate the info[] array in clone_elf(), and use it to
+ adjust the size of the ELF file when we modify the relocation-entry
+ section.
+ */
+ shdr_info_t *shdr_info;
+ GElf_Ehdr old_ehdr_mem; /* store ELF header of original library */
+ GElf_Ehdr ehdr_mem; /* store ELF header of new library */
+ GElf_Phdr *phdr_info;
+ Ebl *ebl;
+ Elf_Data *shstrtab_data;
+ int elf_fd;
+ int newelf_fd; /* fd of output file, -1 if output == NULL */
+ struct stat elf_file_info;
+ GElf_Ehdr elf_hdr, oldelf_hdr;
+ size_t shstrndx;
+ int shnum; /* number of sections */
+ int dry_run; /* 0 if we do not update the files, 1 (default) otherwise */
+
+ section_info_t symtab;
+ section_info_t strtab;
+ section_info_t dynamic;
+ section_info_t hash;
+ section_info_t bss;
+
+ range_list_t *sorted_sections;
+
+ section_info_t *relocation_sections; /* relocation sections in file */
+ int num_relocation_sections; /* number of relocation sections (<= relocation_sections_size) */
+ int relocation_sections_size; /* sice of array -- NOT number of relocs! */
+
+ /* relocation sections that contain relocations that could not be handled.
+ This array is parallel to relocation_sections, and for each entry
+ in that array, it contains a list of relocations that could not be
+ handled.
+ */
+ unfinished_relocation_t *unfinished;
+
+ /* The sections field of these two structuer contains a list of elements
+ of the member variable relocations. */
+ dt_rel_info_t rel;
+ dt_rel_info_t jmprel;
+
+ int num_syms; /* number of symbols in symbol table. This is the length of
+ both exports[] and satisfied[] arrays. */
+
+ /* This is an array that contains one element for each library dependency
+ listed in the executable or shared library. */
+ source_t **lib_deps; /* list of library dependencies */
+ int num_lib_deps; /* actual number of library dependencies */
+ int lib_deps_size; /* size of lib_deps array--NOT actual number of deps! */
+
+ /* This is zero for executables. For shared libraries, it is the address
+ at which the library was prelinked. */
+ unsigned base;
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ /* When we read in a file, if it has the prelinked tag, we set prelinked
+ to 1 and the prelink address in the tag to prelink_base. This address
+ must match the value of base that we choose. */
+ int prelinked;
+ long prelink_base; /* valid if prelinked != 0 */
+#endif/*SUPPORT_ANDROID_PRELINK_TAGS*/
+};
+
+extern void find_section(source_t *source, Elf64_Addr address,
+ Elf_Scn **scn,
+ GElf_Shdr *shdr,
+ Elf_Data **data);
+
+#endif/*SOURCE_H*/
diff --git a/tools/apriori/tweak.h b/tools/apriori/tweak.h
new file mode 100755
index 000000000..3afedee5b
--- /dev/null
+++ b/tools/apriori/tweak.h
@@ -0,0 +1,15 @@
+#ifndef TWEAK_H
+#define TWEAK_H
+
+#include <source.h>
+
+/* This function will break up the .bss section into multiple subsegments,
+ depending on whether the .bss segment contains copy-relocated symbols. This
+ will produce a nonstandard ELF file (with multiple .bss sections), tht the
+ linker will need to know how to handle. The return value is the number of
+ segments that the .bss segment was broken into (zero if the .bss segment was
+ not modified. */
+
+int tweak_bss_if_necessary(source_t *source);
+
+#endif/*TWEAK_H*/
diff --git a/tools/atree/Android.mk b/tools/atree/Android.mk
new file mode 100644
index 000000000..d895810f3
--- /dev/null
+++ b/tools/atree/Android.mk
@@ -0,0 +1,20 @@
+# Copyright 2007 The Android Open Source Project
+#
+# Copies files into the directory structure described by a manifest
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ atree.cpp \
+ files.cpp \
+ fs.cpp
+
+LOCAL_STATIC_LIBRARIES := \
+ libhost
+LOCAL_C_INCLUDES := build/libs/host/include
+
+LOCAL_MODULE := atree
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/atree/atree.cpp b/tools/atree/atree.cpp
new file mode 100644
index 000000000..aee2b3ce2
--- /dev/null
+++ b/tools/atree/atree.cpp
@@ -0,0 +1,301 @@
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "options.h"
+#include "files.h"
+#include "fs.h"
+#include <set>
+#include <iostream>
+#include <sstream>
+
+using namespace std;
+
+bool g_debug = false;
+vector<string> g_listFiles;
+vector<string> g_inputBases;
+map<string, string> g_variables;
+string g_outputBase;
+string g_dependency;
+bool g_useHardLinks = false;
+
+const char* USAGE =
+"\n"
+"Usage: atree OPTIONS\n"
+"\n"
+"Options:\n"
+" -f FILELIST Specify one or more files containing the\n"
+" list of files to copy.\n"
+" -I INPUTDIR Specify one or more base directories in\n"
+" which to look for the files\n"
+" -o OUTPUTDIR Specify the directory to copy all of the\n"
+" output files to.\n"
+" -l Use hard links instead of copying the files.\n"
+" -m DEPENDENCY Output a make-formatted file containing the list.\n"
+" of files included. It sets the variable ATREE_FILES.\n"
+" -v VAR=VAL Replaces ${VAR} by VAL when reading input files.\n"
+"\n"
+"FILELIST file format:\n"
+" The FILELIST files contain the list of files that will end up\n"
+" in the final OUTPUTDIR. Atree will look for files in the INPUTDIR\n"
+" directories in the order they are specified.\n"
+"\n"
+" In a FILELIST file, comment lines start with a #. Other lines\n"
+" are of the format:\n"
+"\n"
+" DEST\n"
+" SRC DEST\n"
+" -SRCPATTERN\n"
+"\n"
+" DEST should be path relative to the output directory.\n"
+" If SRC is supplied, the file names can be different.\n"
+" SRCPATTERN is a pattern for the filenames.\n"
+"\n";
+
+int usage()
+{
+ fwrite(USAGE, strlen(USAGE), 1, stderr);
+ return 1;
+}
+
+static bool
+add_variable(const char* arg) {
+ const char* p = arg;
+ while (*p && *p != '=') p++;
+
+ if (*p == 0 || p == arg || p[1] == 0) {
+ return false;
+ }
+
+ ostringstream var;
+ var << "${" << string(arg, p-arg) << "}";
+ g_variables[var.str()] = string(p+1);
+ return true;
+}
+
+int
+main(int argc, char* const* argv)
+{
+ int err;
+ bool done = false;
+ while (!done) {
+ int opt = getopt(argc, argv, "f:I:o:hlm:v:");
+ switch (opt)
+ {
+ case -1:
+ done = true;
+ break;
+ case 'f':
+ g_listFiles.push_back(string(optarg));
+ break;
+ case 'I':
+ g_inputBases.push_back(string(optarg));
+ break;
+ case 'o':
+ if (g_outputBase.length() != 0) {
+ fprintf(stderr, "%s: -o may only be supplied once -- "
+ "-o %s\n", argv[0], optarg);
+ return usage();
+ }
+ g_outputBase = optarg;
+ break;
+ case 'l':
+ g_useHardLinks = true;
+ break;
+ case 'm':
+ if (g_dependency.length() != 0) {
+ fprintf(stderr, "%s: -m may only be supplied once -- "
+ "-m %s\n", argv[0], optarg);
+ return usage();
+ }
+ g_dependency = optarg;
+ break;
+ case 'v':
+ if (!add_variable(optarg)) {
+ fprintf(stderr, "%s Invalid expression in '-v %s': "
+ "expected format is '-v VAR=VALUE'.\n",
+ argv[0], optarg);
+ return usage();
+ }
+ break;
+ default:
+ case '?':
+ case 'h':
+ return usage();
+ }
+ }
+ if (optind != argc) {
+ fprintf(stderr, "%s: invalid argument -- %s\n", argv[0], argv[optind]);
+ return usage();
+ }
+
+ if (g_listFiles.size() == 0) {
+ fprintf(stderr, "%s: At least one -f option must be supplied.\n",
+ argv[0]);
+ return usage();
+ }
+
+ if (g_inputBases.size() == 0) {
+ fprintf(stderr, "%s: At least one -I option must be supplied.\n",
+ argv[0]);
+ return usage();
+ }
+
+ if (g_outputBase.length() == 0) {
+ fprintf(stderr, "%s: -o option must be supplied.\n", argv[0]);
+ return usage();
+ }
+
+
+#if 0
+ for (vector<string>::iterator it=g_listFiles.begin();
+ it!=g_listFiles.end(); it++) {
+ printf("-f \"%s\"\n", it->c_str());
+ }
+ for (vector<string>::iterator it=g_inputBases.begin();
+ it!=g_inputBases.end(); it++) {
+ printf("-I \"%s\"\n", it->c_str());
+ }
+ printf("-o \"%s\"\n", g_outputBase.c_str());
+ if (g_useHardLinks) {
+ printf("-l\n");
+ }
+#endif
+
+ vector<FileRecord> files;
+ vector<FileRecord> more;
+ vector<string> excludes;
+ set<string> directories;
+ set<string> deleted;
+
+ // read file lists
+ for (vector<string>::iterator it=g_listFiles.begin();
+ it!=g_listFiles.end(); it++) {
+ err = read_list_file(*it, g_variables, &files, &excludes);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ // look for input files
+ err = 0;
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ err |= locate(&(*it), g_inputBases);
+
+ }
+
+ // expand the directories that we should copy into a list of files
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ if (it->sourceIsDir) {
+ err |= list_dir(*it, excludes, &more);
+ }
+ }
+ for (vector<FileRecord>::iterator it=more.begin();
+ it!=more.end(); it++) {
+ files.push_back(*it);
+ }
+
+ // get the name and modtime of the output files
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ stat_out(g_outputBase, &(*it));
+ }
+
+ if (err != 0) {
+ return 1;
+ }
+
+ // gather directories
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ if (it->sourceIsDir) {
+ directories.insert(it->outPath);
+ } else {
+ string s = dir_part(it->outPath);
+ if (s != ".") {
+ directories.insert(s);
+ }
+ }
+ }
+
+ // gather files that should become directores and directories that should
+ // become files
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ if (it->outMod != 0 && it->sourceIsDir != it->outIsDir) {
+ deleted.insert(it->outPath);
+ }
+ }
+
+ // delete files
+ for (set<string>::iterator it=deleted.begin();
+ it!=deleted.end(); it++) {
+ if (g_debug) {
+ printf("deleting %s\n", it->c_str());
+ }
+ err = remove_recursively(*it);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ // make directories
+ for (set<string>::iterator it=directories.begin();
+ it!=directories.end(); it++) {
+ if (g_debug) {
+ printf("mkdir %s\n", it->c_str());
+ }
+ err = mkdir_recursively(*it);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ // copy (or link) files
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ if (!it->sourceIsDir) {
+ if (g_debug) {
+ printf("copy %s(%ld) ==> %s(%ld)", it->sourcePath.c_str(),
+ it->sourceMod, it->outPath.c_str(), it->outMod);
+ fflush(stdout);
+ }
+
+ if (it->outMod < it->sourceMod) {
+ err = copy_file(it->sourcePath, it->outPath);
+ if (g_debug) {
+ printf(" done.\n");
+ }
+ if (err != 0) {
+ return err;
+ }
+ } else {
+ if (g_debug) {
+ printf(" skipping.\n");
+ }
+ }
+ }
+ }
+
+ // output the dependency file
+ if (g_dependency.length() != 0) {
+ FILE *f = fopen(g_dependency.c_str(), "w");
+ if (f != NULL) {
+ fprintf(f, "ATREE_FILES := $(ATREE_FILES) \\\n");
+ for (vector<FileRecord>::iterator it=files.begin();
+ it!=files.end(); it++) {
+ if (!it->sourceIsDir) {
+ fprintf(f, "%s \\\n", it->sourcePath.c_str());
+ }
+ }
+ fprintf(f, "\n");
+ fclose(f);
+ } else {
+ fprintf(stderr, "error opening manifest file for write: %s\n",
+ g_dependency.c_str());
+ }
+ }
+
+ return 0;
+}
diff --git a/tools/atree/files.cpp b/tools/atree/files.cpp
new file mode 100644
index 000000000..c675ab7f5
--- /dev/null
+++ b/tools/atree/files.cpp
@@ -0,0 +1,427 @@
+#include "files.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <fnmatch.h>
+
+static bool
+is_comment_line(const char* p)
+{
+ while (*p && isspace(*p)) {
+ p++;
+ }
+ return *p == '#';
+}
+
+static string
+path_append(const string& base, const string& leaf)
+{
+ string full = base;
+ if (base.length() > 0 && leaf.length() > 0) {
+ full += '/';
+ }
+ full += leaf;
+ return full;
+}
+
+static bool
+is_whitespace_line(const char* p)
+{
+ while (*p) {
+ if (!isspace(*p)) {
+ return false;
+ }
+ p++;
+ }
+ return true;
+}
+
+static bool
+is_exclude_line(const char* p) {
+ while (*p) {
+ if (*p == '-') {
+ return true;
+ }
+ else if (isspace(*p)) {
+ p++;
+ }
+ else {
+ return false;
+ }
+ }
+ return false;
+}
+
+void
+split_line(const char* p, vector<string>* out)
+{
+ const char* q = p;
+ enum { WHITE, TEXT } state = WHITE;
+ while (*p) {
+ if (*p == '#') {
+ break;
+ }
+
+ switch (state)
+ {
+ case WHITE:
+ if (!isspace(*p)) {
+ q = p;
+ state = TEXT;
+ }
+ break;
+ case TEXT:
+ if (isspace(*p)) {
+ if (q != p) {
+ out->push_back(string(q, p-q));
+ }
+ state = WHITE;
+ }
+ break;
+ }
+ p++;
+ }
+ if (state == TEXT) {
+ out->push_back(string(q, p-q));
+ }
+}
+
+static void
+add_file(vector<FileRecord>* files, const string& listFile, int listLine,
+ const string& sourceName, const string& outName)
+{
+ FileRecord rec;
+ rec.listFile = listFile;
+ rec.listLine = listLine;
+ rec.sourceName = sourceName;
+ rec.outName = outName;
+ files->push_back(rec);
+}
+
+static string
+replace_variables(const string& input,
+ const map<string, string>& variables,
+ bool* error) {
+ if (variables.empty()) {
+ return input;
+ }
+
+ // Abort if the variable prefix is not found
+ if (input.find("${") == string::npos) {
+ return input;
+ }
+
+ string result = input;
+
+ // Note: rather than be fancy to detect recursive replacements,
+ // we simply iterate till a given threshold is met.
+
+ int retries = 1000;
+ bool did_replace;
+
+ do {
+ did_replace = false;
+ for (map<string, string>::const_iterator it = variables.begin();
+ it != variables.end(); ++it) {
+ string::size_type pos = 0;
+ while((pos = result.find(it->first, pos)) != string::npos) {
+ result = result.replace(pos, it->first.length(), it->second);
+ pos += it->second.length();
+ did_replace = true;
+ }
+ }
+ if (did_replace && --retries == 0) {
+ *error = true;
+ fprintf(stderr, "Recursive replacement detected during variables "
+ "substitution. Full list of variables is: ");
+
+ for (map<string, string>::const_iterator it = variables.begin();
+ it != variables.end(); ++it) {
+ fprintf(stderr, " %s=%s\n",
+ it->first.c_str(), it->second.c_str());
+ }
+
+ return result;
+ }
+ } while (did_replace);
+
+ return result;
+}
+
+int
+read_list_file(const string& filename,
+ const map<string, string>& variables,
+ vector<FileRecord>* files,
+ vector<string>* excludes)
+{
+ int err = 0;
+ FILE* f = NULL;
+ long size;
+ char* buf = NULL;
+ char *p, *q;
+ int i, lineCount;
+
+ f = fopen(filename.c_str(), "r");
+ if (f == NULL) {
+ fprintf(stderr, "Could not open list file (%s): %s\n",
+ filename.c_str(), strerror(errno));
+ err = errno;
+ goto cleanup;
+ }
+
+ err = fseek(f, 0, SEEK_END);
+ if (err != 0) {
+ fprintf(stderr, "Could not seek to the end of file %s. (%s)\n",
+ filename.c_str(), strerror(errno));
+ err = errno;
+ goto cleanup;
+ }
+
+ size = ftell(f);
+
+ err = fseek(f, 0, SEEK_SET);
+ if (err != 0) {
+ fprintf(stderr, "Could not seek to the beginning of file %s. (%s)\n",
+ filename.c_str(), strerror(errno));
+ err = errno;
+ goto cleanup;
+ }
+
+ buf = (char*)malloc(size+1);
+ if (buf == NULL) {
+ // (potentially large)
+ fprintf(stderr, "out of memory (%ld)\n", size);
+ err = ENOMEM;
+ goto cleanup;
+ }
+
+ if (1 != fread(buf, size, 1, f)) {
+ fprintf(stderr, "error reading file %s. (%s)\n",
+ filename.c_str(), strerror(errno));
+ err = errno;
+ goto cleanup;
+ }
+
+ // split on lines
+ p = buf;
+ q = buf+size;
+ lineCount = 0;
+ while (p<q) {
+ if (*p == '\r' || *p == '\n') {
+ *p = '\0';
+ lineCount++;
+ }
+ p++;
+ }
+
+ // read lines
+ p = buf;
+ for (i=0; i<lineCount; i++) {
+ int len = strlen(p);
+ q = p + len + 1;
+ if (is_whitespace_line(p) || is_comment_line(p)) {
+ ;
+ }
+ else if (is_exclude_line(p)) {
+ while (*p != '-') p++;
+ p++;
+ excludes->push_back(string(p));
+ }
+ else {
+ vector<string> words;
+
+ split_line(p, &words);
+
+#if 0
+ printf("[ ");
+ for (size_t k=0; k<words.size(); k++) {
+ printf("'%s' ", words[k].c_str());
+ }
+ printf("]\n");
+#endif
+
+ if (words.size() == 1) {
+ // pattern: DEST
+ bool error = false;
+ string w0 = replace_variables(words[0], variables, &error);
+ if (error) {
+ err = 1;
+ goto cleanup;
+ }
+ add_file(files, filename, i+1, w0, w0);
+ }
+ else if (words.size() == 2) {
+ // pattern: SRC DEST
+ bool error = false;
+ string w0, w1;
+ w0 = replace_variables(words[0], variables, &error);
+ if (!error) {
+ w1 = replace_variables(words[1], variables, &error);
+ }
+ if (error) {
+ err = 1;
+ goto cleanup;
+ }
+ add_file(files, filename, i+1, w0, w1);
+ }
+ else {
+ fprintf(stderr, "%s:%d: bad format: %s\n", filename.c_str(),
+ i+1, p);
+ err = 1;
+ }
+ }
+ p = q;
+ }
+
+cleanup:
+ if (buf != NULL) {
+ free(buf);
+ }
+ if (f != NULL) {
+ fclose(f);
+ }
+ return err;
+}
+
+
+int
+locate(FileRecord* rec, const vector<string>& search)
+{
+ int err;
+
+ for (vector<string>::const_iterator it=search.begin();
+ it!=search.end(); it++) {
+ string full = path_append(*it, rec->sourceName);
+ struct stat st;
+ err = stat(full.c_str(), &st);
+ if (err == 0) {
+ rec->sourceBase = *it;
+ rec->sourcePath = full;
+ rec->sourceMod = st.st_mtime;
+ rec->sourceIsDir = S_ISDIR(st.st_mode);
+ return 0;
+ }
+ }
+
+ fprintf(stderr, "%s:%d: couldn't locate source file: %s\n",
+ rec->listFile.c_str(), rec->listLine, rec->sourceName.c_str());
+ return 1;
+}
+
+void
+stat_out(const string& base, FileRecord* rec)
+{
+ rec->outPath = path_append(base, rec->outName);
+
+ int err;
+ struct stat st;
+ err = stat(rec->outPath.c_str(), &st);
+ if (err == 0) {
+ rec->outMod = st.st_mtime;
+ rec->outIsDir = S_ISDIR(st.st_mode);
+ } else {
+ rec->outMod = 0;
+ rec->outIsDir = false;
+ }
+}
+
+string
+dir_part(const string& filename)
+{
+ int pos = filename.rfind('/');
+ if (pos <= 0) {
+ return ".";
+ }
+ return filename.substr(0, pos);
+}
+
+static void
+add_more(const string& entry, bool isDir,
+ const FileRecord& rec, vector<FileRecord>*more)
+{
+ FileRecord r;
+ r.listFile = rec.listFile;
+ r.listLine = rec.listLine;
+ r.sourceName = path_append(rec.sourceName, entry);
+ r.sourcePath = path_append(rec.sourceBase, r.sourceName);
+ struct stat st;
+ int err = stat(r.sourcePath.c_str(), &st);
+ if (err == 0) {
+ r.sourceMod = st.st_mtime;
+ }
+ r.sourceIsDir = isDir;
+ r.outName = path_append(rec.outName, entry);
+ more->push_back(r);
+}
+
+static bool
+matches_excludes(const char* file, const vector<string>& excludes)
+{
+ for (vector<string>::const_iterator it=excludes.begin();
+ it!=excludes.end(); it++) {
+ if (0 == fnmatch(it->c_str(), file, FNM_PERIOD)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static int
+list_dir(const string& path, const FileRecord& rec,
+ const vector<string>& excludes,
+ vector<FileRecord>* more)
+{
+ int err;
+
+ string full = path_append(rec.sourceBase, rec.sourceName);
+ full = path_append(full, path);
+
+ DIR *d = opendir(full.c_str());
+ if (d == NULL) {
+ return errno;
+ }
+
+ vector<string> dirs;
+
+ struct dirent *ent;
+ while (NULL != (ent = readdir(d))) {
+ if (0 == strcmp(".", ent->d_name)
+ || 0 == strcmp("..", ent->d_name)) {
+ continue;
+ }
+ if (matches_excludes(ent->d_name, excludes)) {
+ continue;
+ }
+ string entry = path_append(path, ent->d_name);
+#ifdef HAVE_DIRENT_D_TYPE
+ bool is_directory = (ent->d_type == DT_DIR);
+#else
+ // If dirent.d_type is missing, then use stat instead
+ struct stat stat_buf;
+ stat(entry.c_str(), &stat_buf);
+ bool is_directory = S_ISDIR(stat_buf.st_mode);
+#endif
+ add_more(entry, is_directory, rec, more);
+ if (is_directory) {
+ dirs.push_back(entry);
+ }
+ }
+ closedir(d);
+
+ for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) {
+ list_dir(*it, rec, excludes, more);
+ }
+
+ return 0;
+}
+
+int
+list_dir(const FileRecord& rec, const vector<string>& excludes,
+ vector<FileRecord>* files)
+{
+ return list_dir("", rec, excludes, files);
+}
diff --git a/tools/atree/files.h b/tools/atree/files.h
new file mode 100644
index 000000000..6480c988c
--- /dev/null
+++ b/tools/atree/files.h
@@ -0,0 +1,39 @@
+#ifndef FILES_H
+#define FILES_H
+
+#include <map>
+#include <string>
+#include <vector>
+#include <sys/types.h>
+
+using namespace std;
+
+struct FileRecord
+{
+ string listFile;
+ int listLine;
+
+ string sourceBase;
+ string sourceName;
+ string sourcePath;
+ bool sourceIsDir;
+ time_t sourceMod;
+
+ string outName;
+ string outPath;
+ time_t outMod;
+ bool outIsDir;
+ unsigned int mode;
+};
+
+int read_list_file(const string& filename,
+ const map<string, string>& variables,
+ vector<FileRecord>* files,
+ vector<string>* excludes);
+int locate(FileRecord* rec, const vector<string>& search);
+void stat_out(const string& base, FileRecord* rec);
+string dir_part(const string& filename);
+int list_dir(const FileRecord& rec, const vector<string>& excludes,
+ vector<FileRecord>* files);
+
+#endif // FILES_H
diff --git a/tools/atree/fs.cpp b/tools/atree/fs.cpp
new file mode 100644
index 000000000..022bd8c0f
--- /dev/null
+++ b/tools/atree/fs.cpp
@@ -0,0 +1,143 @@
+#include "fs.h"
+#include "files.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string>
+#include <vector>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <host/CopyFile.h>
+
+using namespace std;
+
+static bool
+is_dir(const string& path)
+{
+ int err;
+ struct stat st;
+ err = stat(path.c_str(), &st);
+ return err != 0 || S_ISDIR(st.st_mode);
+}
+
+static int
+remove_file(const string& path)
+{
+ int err = unlink(path.c_str());
+ if (err != 0) {
+ fprintf(stderr, "error deleting file %s (%s)\n", path.c_str(),
+ strerror(errno));
+ return errno;
+ }
+ return 0;
+}
+
+int
+remove_recursively(const string& path)
+{
+ int err;
+
+ if (is_dir(path)) {
+ DIR *d = opendir(path.c_str());
+ if (d == NULL) {
+ fprintf(stderr, "error getting directory contents %s (%s)\n",
+ path.c_str(), strerror(errno));
+ return errno;
+ }
+
+ vector<string> files;
+ vector<string> dirs;
+
+ struct dirent *ent;
+ while (NULL != (ent = readdir(d))) {
+ if (0 == strcmp(".", ent->d_name)
+ || 0 == strcmp("..", ent->d_name)) {
+ continue;
+ }
+ string full = path;
+ full += '/';
+ full += ent->d_name;
+#ifdef HAVE_DIRENT_D_TYPE
+ bool is_directory = (ent->d_type == DT_DIR);
+#else
+ // If dirent.d_type is missing, then use stat instead
+ struct stat stat_buf;
+ stat(full.c_str(), &stat_buf);
+ bool is_directory = S_ISDIR(stat_buf.st_mode);
+#endif
+ if (is_directory) {
+ dirs.push_back(full);
+ } else {
+ files.push_back(full);
+ }
+ }
+ closedir(d);
+
+ for (vector<string>::iterator it=files.begin(); it!=files.end(); it++) {
+ err = remove_file(*it);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ for (vector<string>::iterator it=dirs.begin(); it!=dirs.end(); it++) {
+ err = remove_recursively(*it);
+ if (err != 0) {
+ return err;
+ }
+ }
+
+ err = rmdir(path.c_str());
+ if (err != 0) {
+ fprintf(stderr, "error deleting directory %s (%s)\n", path.c_str(),
+ strerror(errno));
+ return errno;
+ }
+ return 0;
+ } else {
+ return remove_file(path);
+ }
+}
+
+int
+mkdir_recursively(const string& path)
+{
+ int err;
+ size_t pos = 0;
+ while (true) {
+ pos = path.find('/', pos);
+ string p = path.substr(0, pos);
+ struct stat st;
+ err = stat(p.c_str(), &st);
+ if (err != 0) {
+ err = mkdir(p.c_str(), 0770);
+ if (err != 0) {
+ fprintf(stderr, "can't create directory %s (%s)\n",
+ path.c_str(), strerror(errno));
+ return errno;
+ }
+ }
+ else if (!S_ISDIR(st.st_mode)) {
+ fprintf(stderr, "can't create directory %s because %s is a file.\n",
+ path.c_str(), p.c_str());
+ return 1;
+ }
+ pos++;
+ if (p == path) {
+ return 0;
+ }
+ }
+}
+
+int
+copy_file(const string& src, const string& dst)
+{
+ int err;
+
+ err = copyFile(src.c_str(), dst.c_str(),
+ COPY_NO_DEREFERENCE | COPY_FORCE | COPY_PERMISSIONS);
+ return err;
+}
diff --git a/tools/atree/fs.h b/tools/atree/fs.h
new file mode 100644
index 000000000..408088088
--- /dev/null
+++ b/tools/atree/fs.h
@@ -0,0 +1,12 @@
+#ifndef FS_H
+#define FS_H
+
+#include <string>
+
+using namespace std;
+
+int remove_recursively(const string& path);
+int mkdir_recursively(const string& path);
+int copy_file(const string& src, const string& dst);
+
+#endif // FS_H
diff --git a/tools/atree/options.h b/tools/atree/options.h
new file mode 100644
index 000000000..a227d0fdc
--- /dev/null
+++ b/tools/atree/options.h
@@ -0,0 +1,14 @@
+#ifndef OPTIONS_H
+#define OPTIONS_H
+
+#include <string>
+#include <vector>
+
+using namespace std;
+
+extern vector<string> g_listFiles;
+extern vector<string> g_inputBases;
+extern string g_outputBase;
+extern bool g_useHardLinks;
+
+#endif // OPTIONS_H
diff --git a/tools/bin2asm/Android.mk b/tools/bin2asm/Android.mk
new file mode 100644
index 000000000..4522a2061
--- /dev/null
+++ b/tools/bin2asm/Android.mk
@@ -0,0 +1,24 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ icudata.c
+
+LOCAL_MODULE := icudata
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/bin2asm/data b/tools/bin2asm/data
new file mode 100644
index 000000000..3be865f80
--- /dev/null
+++ b/tools/bin2asm/data
@@ -0,0 +1,52 @@
+/*
+ * Convert a data file into a .S file suitable for assembly.
+ * This reads from stdin and writes to stdout and takes a single
+ * argument for the name of the symbol in the assembly file.
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ unsigned char buf[4096];
+ size_t amt;
+ size_t i;
+ int col = 0;
+ char *name = argv[1];
+
+ printf("\
+#ifdef __APPLE_CC__\n\
+/*\n\
+ * The mid-2007 version of gcc that ships with Macs requires a\n\
+ * comma on the .section line, but the rest of the world thinks\n\
+ * that's a syntax error. It also wants globals to be explicitly\n\
+ * prefixed with \"_\" as opposed to modern gccs that do the\n\
+ * prefixing for you.\n\
+ */\n\
+.globl _%s\n\
+ .section .rodata,\n\
+ .align 8\n\
+_%s:\n\
+#else\n\
+.globl %s\n\
+ .section .rodata\n\
+ .align 8\n\
+%s:\n\
+#endif\n\
+", name, name, name, name);
+
+ while (! feof(stdin)) {
+ amt = fread(buf, 1, sizeof(buf), stdin);
+ for (i = 0; i < amt; i++) {
+ printf((col == 0) ? ".byte %3d" : ",%3d", buf[i]);
+ col++;
+ if (col == 16) {
+ printf("\n");
+ col = 0;
+ }
+ }
+ }
+
+ if (col != 0) {
+ printf("\n");
+ }
+}
diff --git a/tools/bin2asm/icudata.c b/tools/bin2asm/icudata.c
new file mode 100644
index 000000000..ecd1b4b05
--- /dev/null
+++ b/tools/bin2asm/icudata.c
@@ -0,0 +1,72 @@
+/*
+ * Convert a data file into a .S file suitable for assembly.
+ * This reads from stdin and writes to stdout and takes a single
+ * argument for the name of the symbol in the assembly file.
+ */
+
+#include <stdio.h>
+
+int main(int argc, char *argv[]) {
+ unsigned char buf[4096];
+ size_t amt;
+ size_t i;
+ int col = 0;
+ char *name;
+
+ if (argc != 2) {
+ fprintf(stderr, "usage: %s NAME < DAT_FILE > ASM_FILE\n", argv[0]);
+ for (i=0; i<argc; i++) {
+ fprintf(stderr, " '%s'", argv[i]);
+ }
+ fprintf(stderr, "\n");
+ return 1;
+ }
+
+ name = argv[1];
+
+ printf("\
+#ifdef __APPLE_CC__\n\
+/*\n\
+ * The mid-2007 version of gcc that ships with Macs requires a\n\
+ * comma on the .section line, but the rest of the world thinks\n\
+ * that's a syntax error. It also wants globals to be explicitly\n\
+ * prefixed with \"_\" as opposed to modern gccs that do the\n\
+ * prefixing for you.\n\
+ */\n\
+.globl _%s\n\
+ .section .rodata,\n\
+ .align 8\n\
+_%s:\n\
+#else\n\
+.globl %s\n\
+ .section .rodata\n\
+ .align 8\n\
+%s:\n\
+#endif\n\
+", name, name, name, name);
+
+ while (! feof(stdin)) {
+ amt = fread(buf, 1, sizeof(buf), stdin);
+ for (i = 0; i < amt; i++) {
+ if (col == 0) {
+ printf(".byte ");
+ }
+ printf("0x%02x", buf[i]);
+ col++;
+ if (col == 16) {
+ printf("\n");
+ col = 0;
+ } else if (col % 4 == 0) {
+ printf(", ");
+ } else {
+ printf(",");
+ }
+ }
+ }
+
+ if (col != 0) {
+ printf("\n");
+ }
+
+ return 0;
+}
diff --git a/tools/buildinfo.sh b/tools/buildinfo.sh
new file mode 100755
index 000000000..4e99bf5c0
--- /dev/null
+++ b/tools/buildinfo.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+echo "# begin build properties"
+echo "# autogenerated by buildinfo.sh"
+
+echo "ro.build.id=$BUILD_ID"
+echo "ro.build.display.id=$BUILD_DISPLAY_ID"
+echo "ro.build.version.incremental=$BUILD_NUMBER"
+echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"
+echo "ro.build.version.release=$PLATFORM_VERSION"
+echo "ro.build.date=`date`"
+echo "ro.build.date.utc=`date +%s`"
+echo "ro.build.type=$TARGET_BUILD_TYPE"
+echo "ro.build.user=$USER"
+echo "ro.build.host=`hostname`"
+echo "ro.build.tags=$BUILD_VERSION_TAGS"
+echo "ro.product.model=$PRODUCT_MODEL"
+echo "ro.product.brand=$PRODUCT_BRAND"
+echo "ro.product.name=$PRODUCT_NAME"
+echo "ro.product.device=$TARGET_DEVICE"
+echo "ro.product.board=$TARGET_BOOTLOADER_BOARD_NAME"
+echo "ro.product.manufacturer=$PRODUCT_MANUFACTURER"
+echo "ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
+echo "ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
+echo "ro.board.platform=$TARGET_BOARD_PLATFORM"
+
+echo "# ro.build.product is obsolete; use ro.product.device"
+echo "ro.build.product=$TARGET_DEVICE"
+
+echo "# Do not try to parse ro.build.description or .fingerprint"
+echo "ro.build.description=$PRIVATE_BUILD_DESC"
+echo "ro.build.fingerprint=$BUILD_FINGERPRINT"
+
+echo "# end build properties"
diff --git a/tools/check_builds.sh b/tools/check_builds.sh
new file mode 100755
index 000000000..14dfec6a8
--- /dev/null
+++ b/tools/check_builds.sh
@@ -0,0 +1,67 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Usage:
+#
+# Source this file into your environment. Then:
+#
+# $ golden_builds sdk-sdk generic-eng generic-userdebug dream-eng
+#
+# will build a set of combos. This might take a while. Then you can
+# go make changes, and run:
+#
+# $ check_builds sdk-sdk generic-eng generic-userdebug dream-eng
+#
+# Go get dinner, and when you get back, there will be a file
+# test-builds/sizes.html that has a pretty chart of which files are
+# in which tree, and how big they are. In that chart, cells for files
+# that are missing are red, and rows where the file sizes are not all
+# the same will be blue.
+#
+
+TEST_BUILD_DIR=test-builds
+
+function do_builds
+{
+ PREFIX=$1
+ shift
+ while [ -n "$1" ]
+ do
+ rm -rf $TEST_BUILD_DIR/$PREFIX-$1
+ make -j6 PRODUCT-$1 dist DIST_DIR=$TEST_BUILD_DIR/$PREFIX-$1
+ if [ $? -ne 0 ] ; then
+ echo FAILED
+ return
+ fi
+ shift
+ done
+}
+
+function golden_builds
+{
+ rm -rf $TEST_BUILD_DIR/golden-* $TEST_BUILD_DIR/dist-*
+ do_builds golden "$@"
+}
+
+function check_builds
+{
+ rm -rf $TEST_BUILD_DIR/dist-*
+ do_builds dist "$@"
+ build/tools/compare_fileslist.py \
+ $TEST_BUILD_DIR/golden-*/installed-files.txt \
+ $TEST_BUILD_DIR/dist-*/installed-files.txt \
+ > $TEST_BUILD_DIR/sizes.html
+}
+
diff --git a/tools/check_prereq/Android.mk b/tools/check_prereq/Android.mk
new file mode 100644
index 000000000..abedff6d2
--- /dev/null
+++ b/tools/check_prereq/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_SRC_FILES := check_prereq.c
+LOCAL_MODULE := check_prereq
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+LOCAL_C_INCLUDES +=
+LOCAL_STATIC_LIBRARIES += libcutils libc
+
+include $(BUILD_EXECUTABLE)
+
+endif # !TARGET_SIMULATOR
+
diff --git a/tools/check_prereq/check_prereq.c b/tools/check_prereq/check_prereq.c
new file mode 100644
index 000000000..d7b8918fc
--- /dev/null
+++ b/tools/check_prereq/check_prereq.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/system_properties.h>
+#include <cutils/properties.h>
+
+// Compare the timestamp of the new build (passed on the command line)
+// against the current value of ro.build.date.utc. Exit successfully
+// if the new build is newer than the current build (or if the
+// timestamps are the same).
+int main(int argc, char** argv) {
+ if (argc != 2) {
+ usage:
+ fprintf(stderr, "usage: %s <timestamp>\n", argv[0]);
+ return 2;
+ }
+
+ char value[PROPERTY_VALUE_MAX];
+ char* default_value = "0";
+
+ property_get("ro.build.date.utc", value, default_value);
+
+ long current = strtol(value, NULL, 10);
+ char* end;
+ long install = strtol(argv[1], &end, 10);
+
+ printf("current build time: [%ld] new build time: [%ld]\n",
+ current, install);
+
+ return (*end == 0 && current > 0 && install >= current) ? 0 : 1;
+}
diff --git a/tools/compare_fileslist.py b/tools/compare_fileslist.py
new file mode 100755
index 000000000..1f507d864
--- /dev/null
+++ b/tools/compare_fileslist.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import cgi, os, string, sys
+
+def IsDifferent(row):
+ val = None
+ for v in row:
+ if v:
+ if not val:
+ val = v
+ else:
+ if val != v:
+ return True
+ return False
+
+def main(argv):
+ inputs = argv[1:]
+ data = {}
+ index = 0
+ for input in inputs:
+ f = file(input, "r")
+ lines = f.readlines()
+ f.close()
+ lines = map(string.split, lines)
+ lines = map(lambda (x,y): (y,int(x)), lines)
+ for fn,sz in lines:
+ if not data.has_key(fn):
+ data[fn] = {}
+ data[fn][index] = sz
+ index = index + 1
+ rows = []
+ for fn,sizes in data.iteritems():
+ row = [fn]
+ for i in range(0,index):
+ if sizes.has_key(i):
+ row.append(sizes[i])
+ else:
+ row.append(None)
+ rows.append(row)
+ rows = sorted(rows, key=lambda x: x[0])
+ print """<html>
+ <head>
+ <style type="text/css">
+ .fn, .sz, .z, .d {
+ padding-left: 10px;
+ padding-right: 10px;
+ }
+ .sz, .z, .d {
+ text-align: right;
+ }
+ .fn {
+ background-color: #ffffdd;
+ }
+ .sz {
+ background-color: #ffffcc;
+ }
+ .z {
+ background-color: #ffcccc;
+ }
+ .d {
+ background-color: #99ccff;
+ }
+ </style>
+ </head>
+ <body>
+ """
+ print "<table>"
+ print "<tr>"
+ for input in inputs:
+ combo = input.split(os.path.sep)[1]
+ print " <td class='fn'>%s</td>" % cgi.escape(combo)
+ print "</tr>"
+
+ for row in rows:
+ print "<tr>"
+ for sz in row[1:]:
+ if not sz:
+ print " <td class='z'>&nbsp;</td>"
+ elif IsDifferent(row[1:]):
+ print " <td class='d'>%d</td>" % sz
+ else:
+ print " <td class='sz'>%d</td>" % sz
+ print " <td class='fn'>%s</td>" % cgi.escape(row[0])
+ print "</tr>"
+ print "</table>"
+ print "</body></html>"
+
+if __name__ == '__main__':
+ main(sys.argv)
+
+
diff --git a/tools/dexpreopt/Android.mk b/tools/dexpreopt/Android.mk
new file mode 100644
index 000000000..40aeee2e3
--- /dev/null
+++ b/tools/dexpreopt/Android.mk
@@ -0,0 +1,38 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+ifneq ($(TARGET_SIMULATOR),true)
+ifneq ($(DISABLE_DEXPREOPT),true)
+
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+LOCAL_PREBUILT_EXECUTABLES := dexpreopt.py
+include $(BUILD_SYSTEM)/host_prebuilt.mk
+DEXPREOPT := $(LOCAL_INSTALLED_MODULE)
+
+# The script uses some other tools; make sure that they're
+# installed along with it.
+tools := \
+ emulator$(HOST_EXECUTABLE_SUFFIX)
+
+$(DEXPREOPT): | $(addprefix $(HOST_OUT_EXECUTABLES)/,$(tools))
+
+subdir_makefiles := \
+ $(LOCAL_PATH)/dexopt-wrapper/Android.mk \
+ $(LOCAL_PATH)/afar/Android.mk
+include $(subdir_makefiles)
+
+endif # !disable_dexpreopt
+endif # !sim
diff --git a/tools/dexpreopt/Config.mk b/tools/dexpreopt/Config.mk
new file mode 100644
index 000000000..c6639b28f
--- /dev/null
+++ b/tools/dexpreopt/Config.mk
@@ -0,0 +1,146 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Included by config/Makefile.
+# Defines the pieces necessary for the dexpreopt process.
+#
+# inputs: INSTALLED_RAMDISK_TARGET, BUILT_SYSTEMIMAGE_UNOPT
+# outputs: BUILT_SYSTEMIMAGE, SYSTEMIMAGE_SOURCE_DIR
+#
+LOCAL_PATH := $(my-dir)
+
+# TODO: see if we can make the .odex files not be product-specific.
+# They can't be completely common, though, because their format
+# depends on the architecture of the target system; ARM and x86
+# would have different versions.
+intermediates := \
+ $(call intermediates-dir-for,PACKAGING,dexpreopt)
+dexpreopt_system_dir := $(intermediates)/system
+built_afar := $(call intermediates-dir-for,EXECUTABLES,afar)/afar
+built_dowrapper := \
+ $(call intermediates-dir-for,EXECUTABLES,dexopt-wrapper)/dexopt-wrapper
+
+# Generate a stripped-down init.rc based on the real one.
+dexpreopt_initrc := $(intermediates)/etc/init.rc
+geninitrc_script := $(LOCAL_PATH)/geninitrc.awk
+$(dexpreopt_initrc): script := $(geninitrc_script)
+$(dexpreopt_initrc): system/core/rootdir/init.rc $(geninitrc_script)
+ @echo "Dexpreopt init.rc: $@"
+ @mkdir -p $(dir $@)
+ $(hide) awk -f $(script) < $< > $@
+
+BUILT_DEXPREOPT_RAMDISK := $(intermediates)/ramdisk.img
+$(BUILT_DEXPREOPT_RAMDISK): intermediates := $(intermediates)
+$(BUILT_DEXPREOPT_RAMDISK): dexpreopt_root_out := $(intermediates)/root
+$(BUILT_DEXPREOPT_RAMDISK): dexpreopt_initrc := $(dexpreopt_initrc)
+$(BUILT_DEXPREOPT_RAMDISK): built_afar := $(built_afar)
+$(BUILT_DEXPREOPT_RAMDISK): built_dowrapper := $(built_dowrapper)
+$(BUILT_DEXPREOPT_RAMDISK): \
+ $(INSTALLED_RAMDISK_TARGET) \
+ $(dexpreopt_initrc) \
+ $(built_afar) \
+ $(built_dowrapper) \
+ | $(MKBOOTFS) $(ACP)
+$(BUILT_DEXPREOPT_RAMDISK):
+ @echo "Dexpreopt ramdisk: $@"
+ $(hide) rm -f $@
+ $(hide) rm -rf $(dexpreopt_root_out)
+ $(hide) mkdir -p $(dexpreopt_root_out)
+ $(hide) $(ACP) -rd $(TARGET_ROOT_OUT) $(intermediates)
+ $(hide) $(ACP) -f $(dexpreopt_initrc) $(dexpreopt_root_out)/
+ $(hide) $(ACP) $(built_afar) $(dexpreopt_root_out)/sbin/
+ $(hide) $(ACP) $(built_dowrapper) $(dexpreopt_root_out)/sbin/
+ $(MKBOOTFS) $(dexpreopt_root_out) | gzip > $@
+
+sign_dexpreopt := true
+ifdef sign_dexpreopt
+ # Such a huge hack. We need to re-sign the .apks with the
+ # same certs that they were originally signed with.
+ dexpreopt_package_certs_file := $(intermediates)/package-certs
+ $(shell mkdir -p $(intermediates))
+ $(shell rm -f $(dexpreopt_package_certs_file))
+ $(foreach p,$(PACKAGES),\
+ $(shell echo "$(p) $(PACKAGES.$(p).CERTIFICATE) $(PACKAGES.$(p).PRIVATE_KEY)" >> $(dexpreopt_package_certs_file)))
+endif
+
+# Build an optimized image from the unoptimized image
+BUILT_DEXPREOPT_SYSTEMIMAGE := $(intermediates)/system.img
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): $(BUILT_SYSTEMIMAGE_UNOPT)
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): $(BUILT_DEXPREOPT_RAMDISK)
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): | $(DEXPREOPT) $(ACP) $(ZIPALIGN)
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): SYSTEM_DIR := $(dexpreopt_system_dir)
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): DEXPREOPT_TMP := $(intermediates)/emutmp
+ifdef sign_dexpreopt
+$(BUILT_DEXPREOPT_SYSTEMIMAGE): | $(SIGNAPK_JAR)
+endif
+$(BUILT_DEXPREOPT_SYSTEMIMAGE):
+ @rm -f $@
+ @echo "dexpreopt: copy system to $(SYSTEM_DIR)"
+ @rm -rf $(SYSTEM_DIR)
+ @mkdir -p $(dir $(SYSTEM_DIR))
+ $(hide) $(ACP) -rd $(TARGET_OUT) $(SYSTEM_DIR)
+ @echo "dexpreopt: optimize dex files"
+ @rm -rf $(DEXPREOPT_TMP)
+ @mkdir -p $(DEXPREOPT_TMP)
+ $(hide) \
+ PATH=$(HOST_OUT_EXECUTABLES):$$PATH \
+ $(DEXPREOPT) \
+ --kernel prebuilt/android-arm/kernel/kernel-qemu \
+ --ramdisk $(BUILT_DEXPREOPT_RAMDISK) \
+ --image $(BUILT_SYSTEMIMAGE_UNOPT) \
+ --system $(PRODUCT_OUT) \
+ --tmpdir $(DEXPREOPT_TMP) \
+ --outsystemdir $(SYSTEM_DIR)
+ifdef sign_dexpreopt
+ @echo "dexpreopt: re-sign apk files"
+ $(hide) \
+ export PATH=$(HOST_OUT_EXECUTABLES):$$PATH; \
+ for apk in $(SYSTEM_DIR)/app/*.apk; do \
+ packageName=`basename $$apk`; \
+ packageName=`echo $$packageName | sed -e 's/.apk$$//'`; \
+ cert=`grep "^$$packageName " $(dexpreopt_package_certs_file) | \
+ awk '{print $$2}'`; \
+ pkey=`grep "^$$packageName " $(dexpreopt_package_certs_file) | \
+ awk '{print $$3}'`; \
+ if [ "$$cert" -a "$$pkey" ]; then \
+ echo "dexpreopt: re-sign app/"$$packageName".apk"; \
+ tmpApk=$$apk~; \
+ rm -f $$tmpApk; \
+ java -jar $(SIGNAPK_JAR) $$cert $$pkey $$apk $$tmpApk || \
+ exit 11; \
+ mv -f $$tmpApk $$apk; \
+ else \
+ echo "dexpreopt: no keys for app/"$$packageName".apk"; \
+ rm $(SYSTEM_DIR)/app/$$packageName.* && \
+ cp $(TARGET_OUT)/app/$$packageName.apk \
+ $(SYSTEM_DIR)/app || exit 12; \
+ fi; \
+ tmpApk=$$apk~; \
+ rm -f $$tmpApk; \
+ $(ZIPALIGN) -f 4 $$apk $$tmpApk || exit 13; \
+ mv -f $$tmpApk $$apk; \
+ done
+endif
+ @echo "Dexpreopt system image: $@"
+ $(hide) $(MKYAFFS2) -f $(SYSTEM_DIR) $@
+
+.PHONY: dexpreoptimage
+dexpreoptimage: $(BUILT_DEXPREOPT_SYSTEMIMAGE)
+
+# Tell our caller to use the optimized systemimage
+BUILT_SYSTEMIMAGE := $(BUILT_DEXPREOPT_SYSTEMIMAGE)
+SYSTEMIMAGE_SOURCE_DIR := $(dexpreopt_system_dir)
diff --git a/tools/dexpreopt/afar/Android.mk b/tools/dexpreopt/afar/Android.mk
new file mode 100644
index 000000000..d224675b4
--- /dev/null
+++ b/tools/dexpreopt/afar/Android.mk
@@ -0,0 +1,29 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ main.c
+
+# Just for adler32()
+LOCAL_C_INCLUDES := external/zlib
+LOCAL_SHARED_LIBRARIES := libz
+
+LOCAL_MODULE := afar
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/dexpreopt/afar/main.c b/tools/dexpreopt/afar/main.c
new file mode 100644
index 000000000..d66d59c8c
--- /dev/null
+++ b/tools/dexpreopt/afar/main.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+
+#include <stdarg.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <zlib.h> // for adler32()
+
+static int verbose = 0;
+
+/*
+ * Android File Archive format:
+ *
+ * magic[5]: 'A' 'F' 'A' 'R' '\n'
+ * version[4]: 0x00 0x00 0x00 0x01
+ * for each file:
+ * file magic[4]: 'F' 'I' 'L' 'E'
+ * namelen[4]: Length of file name, including NUL byte (big-endian)
+ * name[*]: NUL-terminated file name
+ * datalen[4]: Length of file (big-endian)
+ * data[*]: Unencoded file data
+ * adler32[4]: adler32 of the unencoded file data (big-endian)
+ * file end magic[4]: 'f' 'i' 'l' 'e'
+ * end magic[4]: 'E' 'N' 'D' 0x00
+ *
+ * This format is about as simple as possible; it was designed to
+ * make it easier to transfer multiple files over an stdin/stdout
+ * pipe to another process, so word-alignment wasn't necessary.
+ */
+
+static void
+die(const char *why, ...)
+{
+ va_list ap;
+
+ va_start(ap, why);
+ fprintf(stderr, "error: ");
+ vfprintf(stderr, why, ap);
+ fprintf(stderr, "\n");
+ va_end(ap);
+ exit(1);
+}
+
+static void
+write_big_endian(size_t v)
+{
+ putchar((v >> 24) & 0xff);
+ putchar((v >> 16) & 0xff);
+ putchar((v >> 8) & 0xff);
+ putchar( v & 0xff);
+}
+
+static void
+_eject(struct stat *s, char *out, int olen, char *data, size_t datasize)
+{
+ unsigned long adler;
+
+ /* File magic.
+ */
+ printf("FILE");
+
+ /* Name length includes the NUL byte.
+ */
+ write_big_endian(olen + 1);
+
+ /* File name and terminating NUL.
+ */
+ printf("%s", out);
+ putchar('\0');
+
+ /* File length.
+ */
+ write_big_endian(datasize);
+
+ /* File data.
+ */
+ if (fwrite(data, 1, datasize, stdout) != datasize) {
+ die("Error writing file data");
+ }
+
+ /* Checksum.
+ */
+ adler = adler32(0, NULL, 0);
+ adler = adler32(adler, (unsigned char *)data, datasize);
+ write_big_endian(adler);
+
+ /* File end magic.
+ */
+ printf("file");
+}
+
+static void _archive(char *in, int ilen);
+
+static void
+_archive_dir(char *in, int ilen)
+{
+ int t;
+ DIR *d;
+ struct dirent *de;
+
+ if (verbose) {
+ fprintf(stderr, "_archive_dir('%s', %d)\n", in, ilen);
+ }
+
+ d = opendir(in);
+ if (d == 0) {
+ die("cannot open directory '%s'", in);
+ }
+
+ while ((de = readdir(d)) != 0) {
+ /* xxx: feature? maybe some dotfiles are okay */
+ if (strcmp(de->d_name, ".") == 0 ||
+ strcmp(de->d_name, "..") == 0)
+ {
+ continue;
+ }
+
+ t = strlen(de->d_name);
+ in[ilen] = '/';
+ memcpy(in + ilen + 1, de->d_name, t + 1);
+
+ _archive(in, ilen + t + 1);
+
+ in[ilen] = '\0';
+ }
+}
+
+static void
+_archive(char *in, int ilen)
+{
+ struct stat s;
+
+ if (verbose) {
+ fprintf(stderr, "_archive('%s', %d)\n", in, ilen);
+ }
+
+ if (lstat(in, &s)) {
+ die("could not stat '%s'\n", in);
+ }
+
+ if (S_ISREG(s.st_mode)) {
+ char *tmp;
+ int fd;
+
+ fd = open(in, O_RDONLY);
+ if (fd < 0) {
+ die("cannot open '%s' for read", in);
+ }
+
+ tmp = (char*) malloc(s.st_size);
+ if (tmp == 0) {
+ die("cannot allocate %d bytes", s.st_size);
+ }
+
+ if (read(fd, tmp, s.st_size) != s.st_size) {
+ die("cannot read %d bytes", s.st_size);
+ }
+
+ _eject(&s, in, ilen, tmp, s.st_size);
+
+ free(tmp);
+ close(fd);
+ } else if (S_ISDIR(s.st_mode)) {
+ _archive_dir(in, ilen);
+ } else {
+ /* We don't handle links, etc. */
+ die("Unknown '%s' (mode %d)?\n", in, s.st_mode);
+ }
+}
+
+void archive(const char *start)
+{
+ char in[8192];
+
+ strcpy(in, start);
+
+ _archive_dir(in, strlen(in));
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct termios old_termios;
+
+ if (argc == 1) {
+ die("usage: %s <dir-list>", argv[0]);
+ }
+ argc--;
+ argv++;
+
+ /* Force stdout into raw mode.
+ */
+ struct termios s;
+ if (tcgetattr(1, &s) < 0) {
+ die("Could not get termios for stdout");
+ }
+ old_termios = s;
+ cfmakeraw(&s);
+ if (tcsetattr(1, TCSANOW, &s) < 0) {
+ die("Could not set termios for stdout");
+ }
+
+ /* Print format magic and version.
+ */
+ printf("AFAR\n");
+ write_big_endian(1);
+
+ while (argc-- > 0) {
+ archive(*argv++);
+ }
+
+ /* Print end magic.
+ */
+ printf("END");
+ putchar('\0');
+
+ /* Restore stdout.
+ */
+ if (tcsetattr(1, TCSANOW, &old_termios) < 0) {
+ die("Could not restore termios for stdout");
+ }
+
+ return 0;
+}
diff --git a/tools/dexpreopt/dexopt-wrapper/Android.mk b/tools/dexpreopt/dexopt-wrapper/Android.mk
new file mode 100644
index 000000000..e6ca389c6
--- /dev/null
+++ b/tools/dexpreopt/dexopt-wrapper/Android.mk
@@ -0,0 +1,36 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ DexOptWrapper.cpp
+
+LOCAL_C_INCLUDES += \
+ dalvik
+
+LOCAL_STATIC_LIBRARIES := \
+ libdex
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_MODULE := dexopt-wrapper
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/tools/dexpreopt/dexopt-wrapper/DexOptWrapper.cpp b/tools/dexpreopt/dexopt-wrapper/DexOptWrapper.cpp
new file mode 100644
index 000000000..fde2d0861
--- /dev/null
+++ b/tools/dexpreopt/dexopt-wrapper/DexOptWrapper.cpp
@@ -0,0 +1,173 @@
+/*
+ * dexopt invocation test.
+ *
+ * You must have BOOTCLASSPATH defined. On the simulator, you will also
+ * need ANDROID_ROOT.
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/file.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include "cutils/properties.h"
+
+//using namespace android;
+
+/*
+ * Privilege reduction function.
+ *
+ * Returns 0 on success, nonzero on failure.
+ */
+static int privFunc(void)
+{
+ printf("--- would reduce privs here\n");
+ return 0;
+}
+
+/*
+ * We're in the child process. exec dexopt.
+ */
+static void runDexopt(int zipFd, int odexFd, const char* inputFileName)
+{
+ static const char* kDexOptBin = "/bin/dexopt";
+ static const int kMaxIntLen = 12; // '-'+10dig+'\0' -OR- 0x+8dig
+ char zipNum[kMaxIntLen];
+ char odexNum[kMaxIntLen];
+ char dexoptFlags[PROPERTY_VALUE_MAX];
+ const char* androidRoot;
+ char* execFile;
+
+ /* pull optional configuration tweaks out of properties */
+ property_get("dalvik.vm.dexopt-flags", dexoptFlags, "");
+
+ /* find dexopt executable; this exists for simulator compatibility */
+ androidRoot = getenv("ANDROID_ROOT");
+ if (androidRoot == NULL)
+ androidRoot = "/system";
+ execFile = (char*) malloc(strlen(androidRoot) + strlen(kDexOptBin) +1);
+ sprintf(execFile, "%s%s", androidRoot, kDexOptBin);
+
+ sprintf(zipNum, "%d", zipFd);
+ sprintf(odexNum, "%d", odexFd);
+
+ execl(execFile, execFile, "--zip", zipNum, odexNum, inputFileName,
+ dexoptFlags, (char*) NULL);
+ fprintf(stderr, "execl(%s) failed: %s\n", kDexOptBin, strerror(errno));
+}
+
+/*
+ * Run dexopt on the specified Jar/APK.
+ *
+ * This uses fork() and exec() to mimic the way this would work in an
+ * installer; in practice for something this simple you could just exec()
+ * unless you really wanted the status messages.
+ *
+ * Returns 0 on success.
+ */
+int doStuff(const char* zipName, const char* odexName)
+{
+ int zipFd, odexFd;
+
+ /*
+ * Open the zip archive and the odex file, creating the latter (and
+ * failing if it already exists). This must be done while we still
+ * have sufficient privileges to read the source file and create a file
+ * in the target directory. The "classes.dex" file will be extracted.
+ */
+ zipFd = open(zipName, O_RDONLY, 0);
+ if (zipFd < 0) {
+ fprintf(stderr, "Unable to open '%s': %s\n", zipName, strerror(errno));
+ return 1;
+ }
+
+ odexFd = open(odexName, O_RDWR | O_CREAT | O_EXCL, 0644);
+ if (odexFd < 0) {
+ fprintf(stderr, "Unable to create '%s': %s\n",
+ odexName, strerror(errno));
+ close(zipFd);
+ return 1;
+ }
+
+ printf("--- BEGIN '%s' (bootstrap=%d) ---\n", zipName, 0);
+
+ /*
+ * Fork a child process.
+ */
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* child -- drop privs */
+ if (privFunc() != 0)
+ exit(66);
+
+ /* lock the input file */
+ if (flock(odexFd, LOCK_EX | LOCK_NB) != 0) {
+ fprintf(stderr, "Unable to lock '%s': %s\n",
+ odexName, strerror(errno));
+ exit(65);
+ }
+
+ runDexopt(zipFd, odexFd, zipName); /* does not return */
+ exit(67); /* usually */
+ } else {
+ /* parent -- wait for child to finish */
+ printf("waiting for verify+opt, pid=%d\n", (int) pid);
+ int status, oldStatus;
+ pid_t gotPid;
+
+ close(zipFd);
+ close(odexFd);
+
+ /*
+ * Wait for the optimization process to finish.
+ */
+ while (true) {
+ gotPid = waitpid(pid, &status, 0);
+ if (gotPid == -1 && errno == EINTR) {
+ printf("waitpid interrupted, retrying\n");
+ } else {
+ break;
+ }
+ }
+ if (gotPid != pid) {
+ fprintf(stderr, "waitpid failed: wanted %d, got %d: %s\n",
+ (int) pid, (int) gotPid, strerror(errno));
+ return 1;
+ }
+
+ if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
+ printf("--- END '%s' (success) ---\n", zipName);
+ return 0;
+ } else {
+ printf("--- END '%s' --- status=0x%04x, process failed\n",
+ zipName, status);
+ return 1;
+ }
+ }
+
+ /* notreached */
+}
+
+/*
+ * Parse args, do stuff.
+ */
+int main(int argc, char** argv)
+{
+ if (argc < 3 || argc > 4) {
+ fprintf(stderr, "Usage: %s <input jar/apk> <output odex> "
+ "[<bootclasspath>]\n\n", argv[0]);
+ fprintf(stderr, "Example: dexopttest "
+ "/system/app/NotePad.apk /system/app/NotePad.odex\n");
+ return 2;
+ }
+
+ if (argc > 3) {
+ setenv("BOOTCLASSPATH", argv[3], 1);
+ }
+
+ return (doStuff(argv[1], argv[2]) != 0);
+}
diff --git a/tools/dexpreopt/dexpreopt.py b/tools/dexpreopt/dexpreopt.py
new file mode 100755
index 000000000..8a80e0629
--- /dev/null
+++ b/tools/dexpreopt/dexpreopt.py
@@ -0,0 +1,981 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Creates optimized versions of APK files.
+
+A tool and associated functions to communicate with an Android
+emulator instance, run commands, and scrape out files.
+
+Requires at least python2.4.
+"""
+
+import array
+import datetime
+import optparse
+import os
+import posix
+import select
+import signal
+import struct
+import subprocess
+import sys
+import tempfile
+import time
+import zlib
+
+
+_emulator_popen = None
+_DEBUG_READ = 1
+
+
+def EnsureTempDir(path=None):
+ """Creates a temporary directory and returns its path.
+
+ Creates any necessary parent directories.
+
+ Args:
+ path: If specified, used as the temporary directory. If not specified,
+ a safe temporary path is created. The caller is responsible for
+ deleting the directory.
+
+ Returns:
+ The path to the new directory, or None if a problem occurred.
+ """
+ if path is None:
+ path = tempfile.mkdtemp('', 'dexpreopt-')
+ elif not os.path.exists(path):
+ os.makedirs(path)
+ elif not os.path.isdir(path):
+ return None
+ return path
+
+
+def CreateZeroedFile(path, length):
+ """Creates the named file and writes <length> zero bytes to it.
+
+ Unlinks the file first if it already exists.
+ Creates its containing directory if necessary.
+
+ Args:
+ path: The path to the file to create.
+ length: The number of zero bytes to write to the file.
+
+ Returns:
+ True on success.
+ """
+ subprocess.call(['rm', '-f', path])
+ d = os.path.dirname(path)
+ if d and not os.path.exists(d): os.makedirs(os.path.dirname(d))
+ # TODO: redirect child's stdout to /dev/null
+ ret = subprocess.call(['dd', 'if=/dev/zero', 'of=%s' % path,
+ 'bs=%d' % length, 'count=1'])
+ return not ret # i.e., ret == 0; i.e., the child exited successfully.
+
+
+def StartEmulator(exe_name='emulator', kernel=None,
+ ramdisk=None, image=None, userdata=None, system=None):
+ """Runs the emulator with the specified arguments.
+
+ Args:
+ exe_name: The name of the emulator to run. May be absolute, relative,
+ or unqualified (and left to exec() to find).
+ kernel: If set, passed to the emulator as "-kernel".
+ ramdisk: If set, passed to the emulator as "-ramdisk".
+ image: If set, passed to the emulator as "-image".
+ userdata: If set, passed to the emulator as "-initdata" and "-data".
+ system: If set, passed to the emulator as "-system".
+
+ Returns:
+ A subprocess.Popen that refers to the emulator process, or None if
+ a problem occurred.
+ """
+ #exe_name = './stuff'
+ args = [exe_name]
+ if kernel: args += ['-kernel', kernel]
+ if ramdisk: args += ['-ramdisk', ramdisk]
+ if image: args += ['-image', image]
+ if userdata: args += ['-initdata', userdata, '-data', userdata]
+ if system: args += ['-system', system]
+ args += ['-no-window', '-netfast', '-noaudio']
+
+ _USE_PIPE = True
+
+ if _USE_PIPE:
+ # Use dedicated fds instead of stdin/out to talk to the
+ # emulator so that the emulator doesn't try to tty-cook
+ # the data.
+ em_stdin_r, em_stdin_w = posix.pipe()
+ em_stdout_r, em_stdout_w = posix.pipe()
+ args += ['-shell-serial', 'fdpair:%d:%d' % (em_stdin_r, em_stdout_w)]
+ else:
+ args += ['-shell']
+
+ # Ensure that this environment variable isn't set;
+ # if it is, the emulator will print the log to stdout.
+ if os.environ.get('ANDROID_LOG_TAGS'):
+ del os.environ['ANDROID_LOG_TAGS']
+
+ try:
+ # bufsize=1 line-buffered, =0 unbuffered,
+ # <0 system default (fully buffered)
+ Trace('Running emulator: %s' % ' '.join(args))
+ if _USE_PIPE:
+ ep = subprocess.Popen(args)
+ else:
+ ep = subprocess.Popen(args, close_fds=True,
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ if ep:
+ if _USE_PIPE:
+ # Hijack the Popen.stdin/.stdout fields to point to our
+ # pipes. These are the same fields that would have been set
+ # if we called Popen() with stdin=subprocess.PIPE, etc.
+ # Note that these names are from the point of view of the
+ # child process.
+ #
+ # Since we'll be using select.select() to read data a byte
+ # at a time, it's important that these files are unbuffered
+ # (bufsize=0). If Popen() took care of the pipes, they're
+ # already unbuffered.
+ ep.stdin = os.fdopen(em_stdin_w, 'w', 0)
+ ep.stdout = os.fdopen(em_stdout_r, 'r', 0)
+ return ep
+ except OSError, e:
+ print >>sys.stderr, 'Could not start emulator:', e
+ return None
+
+
+def IsDataAvailable(fo, timeout=0):
+ """Indicates whether or not data is available to be read from a file object.
+
+ Args:
+ fo: A file object to read from.
+ timeout: The number of seconds to wait for data, or zero for no timeout.
+
+ Returns:
+ True iff data is available to be read.
+ """
+ return select.select([fo], [], [], timeout) == ([fo], [], [])
+
+
+def ConsumeAvailableData(fo):
+ """Reads data from a file object while it's available.
+
+ Stops when no more data is immediately available or upon reaching EOF.
+
+ Args:
+ fo: A file object to read from.
+
+ Returns:
+ An unsigned byte array.array of the data that was read.
+ """
+ buf = array.array('B')
+ while IsDataAvailable(fo):
+ try:
+ buf.fromfile(fo, 1)
+ except EOFError:
+ break
+ return buf
+
+
+def ShowTimeout(timeout, end_time):
+ """For debugging, display the timeout info.
+
+ Args:
+ timeout: the timeout in seconds.
+ end_time: a time.time()-based value indicating when the timeout should
+ expire.
+ """
+ if _DEBUG_READ:
+ if timeout:
+ remaining = end_time - time.time()
+ Trace('ok, time remaining %.1f of %.1f' % (remaining, timeout))
+ else:
+ Trace('ok (no timeout)')
+
+
+def WaitForString(inf, pattern, timeout=0, max_len=0, eat_to_eol=True,
+ reset_on_activity=False):
+ """Reads from a file object and returns when the pattern matches the data.
+
+ Reads a byte at a time to avoid consuming extra data, so do not call
+ this function when you expect the pattern to match a large amount of data.
+
+ Args:
+ inf: The file object to read from.
+ pattern: The string to look for in the input data.
+ May be a tuple of strings.
+ timeout: How long to wait, in seconds. No timeout if it evaluates to False.
+ max_len: Return None if this many bytes have been read without matching.
+ No upper bound if it evaluates to False.
+ eat_to_eol: If true, the input data will be consumed until a '\\n' or EOF
+ is encountered.
+ reset_on_activity: If True, reset the timeout whenever a character is
+ read.
+
+ Returns:
+ The input data matching the expression as an unsigned char array,
+ or None if the operation timed out or didn't match after max_len bytes.
+
+ Raises:
+ IOError: An error occurred reading from the input file.
+ """
+ if timeout:
+ end_time = time.time() + timeout
+ else:
+ end_time = 0
+
+ if _DEBUG_READ:
+ Trace('WaitForString: "%s", %.1f' % (pattern, timeout))
+
+ buf = array.array('B') # unsigned char array
+ eating = False
+ while True:
+ if end_time:
+ remaining = end_time - time.time()
+ if remaining <= 0:
+ Trace('Timeout expired after %.1f seconds' % timeout)
+ return None
+ else:
+ remaining = None
+
+ if IsDataAvailable(inf, remaining):
+ if reset_on_activity and timeout:
+ end_time = time.time() + timeout
+
+ buf.fromfile(inf, 1)
+ if _DEBUG_READ:
+ c = buf.tostring()[-1:]
+ ci = ord(c)
+ if ci < 0x20: c = '.'
+ if _DEBUG_READ > 1:
+ print 'read [%c] 0x%02x' % (c, ci)
+
+ if not eating:
+ if buf.tostring().endswith(pattern):
+ if eat_to_eol:
+ if _DEBUG_READ > 1:
+ Trace('Matched; eating to EOL')
+ eating = True
+ else:
+ ShowTimeout(timeout, end_time)
+ return buf
+ if _DEBUG_READ > 2:
+ print '/%s/ ? "%s"' % (pattern, buf.tostring())
+ else:
+ if buf.tostring()[-1:] == '\n':
+ ShowTimeout(timeout, end_time)
+ return buf
+
+ if max_len and len(buf) >= max_len: return None
+
+
+def WaitForEmulator(ep, timeout=0):
+ """Waits for the emulator to start up and print the first prompt.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ timeout: How long to wait, in seconds. No timeout if it evaluates to False.
+
+ Returns:
+ True on success, False if the timeout occurred.
+ """
+ # Prime the pipe; the emulator doesn't start without this.
+ print >>ep.stdin, ''
+
+ # Wait until the console is ready and the first prompt appears.
+ buf = WaitForString(ep.stdout, '#', timeout=timeout, eat_to_eol=False)
+ if buf:
+ Trace('Saw the prompt: "%s"' % buf.tostring())
+ return True
+ return False
+
+
+def WaitForPrompt(ep, prompt=None, timeout=0, reset_on_activity=False):
+ """Blocks until the prompt appears on ep.stdout or the timeout elapses.
+
+ Args:
+ ep: A subprocess.Popen connection to the emulator process.
+ prompt: The prompt to wait for. If None, uses ep.prompt.
+ timeout: How many seconds to wait for the prompt. Waits forever
+ if timeout is zero.
+ reset_on_activity: If True, reset the timeout whenever a character is
+ read.
+
+ Returns:
+ A string containing the data leading up to the prompt. The string
+ will always end in '\\n'. Returns None if the prompt was not seen
+ within the timeout, or if some other error occurred.
+ """
+ if not prompt: prompt = ep.prompt
+ if prompt:
+ #Trace('waiting for prompt "%s"' % prompt)
+ data = WaitForString(ep.stdout, prompt,
+ timeout=timeout, reset_on_activity=reset_on_activity)
+ if data:
+ # data contains everything on ep.stdout up to and including the prompt,
+ # plus everything up 'til the newline. Scrape out the prompt
+ # and everything that follows, and ensure that the result ends
+ # in a newline (which is important if it would otherwise be empty).
+ s = data.tostring()
+ i = s.rfind(prompt)
+ s = s[:i]
+ if s[-1:] != '\n':
+ s += '\n'
+ if _DEBUG_READ:
+ print 'WaitForPrompt saw """\n%s"""' % s
+ return s
+ return None
+
+
+def ReplaceEmulatorPrompt(ep, prompt=None):
+ """Replaces PS1 in the emulator with a different value.
+
+ This is useful for making the prompt unambiguous; i.e., something
+ that probably won't appear in the output of another command.
+
+ Assumes that the emulator is already sitting at a prompt,
+ waiting for shell input.
+
+ Puts the new prompt in ep.prompt.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ prompt: The new prompt to use
+
+ Returns:
+ True on success, False if the timeout occurred.
+ """
+ if not prompt:
+ prompt = '-----DEXPREOPT-PROMPT-----'
+ print >>ep.stdin, 'PS1="%s\n"' % prompt
+ ep.prompt = prompt
+
+ # Eat the command echo.
+ data = WaitForPrompt(ep, timeout=2)
+ if not data:
+ return False
+
+ # Make sure it's actually there.
+ return WaitForPrompt(ep, timeout=2)
+
+
+def RunEmulatorCommand(ep, cmd, timeout=0):
+ """Sends the command to the emulator's shell and waits for the result.
+
+ Assumes that the emulator is already sitting at a prompt,
+ waiting for shell input.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ cmd: The shell command to run in the emulator.
+ timeout: The number of seconds to wait for the command to complete,
+ or zero for no timeout.
+
+ Returns:
+ If the command ran and returned to the console prompt before the
+ timeout, returns the output of the command as a string.
+ Returns None otherwise.
+ """
+ ConsumeAvailableData(ep.stdout)
+
+ Trace('Running "%s"' % cmd)
+ print >>ep.stdin, '%s' % cmd
+
+ # The console will echo the command.
+ #Trace('Waiting for echo')
+ if WaitForString(ep.stdout, cmd, timeout=timeout):
+ #Trace('Waiting for completion')
+ return WaitForPrompt(ep, timeout=timeout, reset_on_activity=True)
+
+ return None
+
+
+def ReadFileList(ep, dir_list, timeout=0):
+ """Returns a list of emulator files in each dir in dir_list.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ dir_list: List absolute paths to directories to read.
+ timeout: The number of seconds to wait for the command to complete,
+ or zero for no timeout.
+
+ Returns:
+ A list of absolute paths to files in the named directories,
+ in the context of the emulator's filesystem.
+ None on failure.
+ """
+ ret = []
+ for d in dir_list:
+ output = RunEmulatorCommand(ep, 'ls ' + d, timeout=timeout)
+ if not output:
+ Trace('Could not ls ' + d)
+ return None
+ ret += ['%s/%s' % (d, f) for f in output.splitlines()]
+ return ret
+
+
+def DownloadDirectoryHierarchy(ep, src, dest, timeout=0):
+ """Recursively downloads an emulator directory to the local filesystem.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ src: The path on the emulator's filesystem to download from.
+ dest: The path on the local filesystem to download to.
+ timeout: The number of seconds to wait for the command to complete,
+ or zero for no timeout. (CURRENTLY IGNORED)
+
+ Returns:
+ True iff the files downloaded successfully, False otherwise.
+ """
+ ConsumeAvailableData(ep.stdout)
+
+ if not os.path.exists(dest):
+ os.makedirs(dest)
+
+ cmd = 'afar %s' % src
+ Trace('Running "%s"' % cmd)
+ print >>ep.stdin, '%s' % cmd
+
+ # The console will echo the command.
+ #Trace('Waiting for echo')
+ if not WaitForString(ep.stdout, cmd, timeout=timeout):
+ return False
+
+ #TODO: use a signal to support timing out?
+
+ #
+ # Android File Archive format:
+ #
+ # magic[5]: 'A' 'F' 'A' 'R' '\n'
+ # version[4]: 0x00 0x00 0x00 0x01
+ # for each file:
+ # file magic[4]: 'F' 'I' 'L' 'E'
+ # namelen[4]: Length of file name, including NUL byte (big-endian)
+ # name[*]: NUL-terminated file name
+ # datalen[4]: Length of file (big-endian)
+ # data[*]: Unencoded file data
+ # adler32[4]: adler32 of the unencoded file data (big-endian)
+ # file end magic[4]: 'f' 'i' 'l' 'e'
+ # end magic[4]: 'E' 'N' 'D' 0x00
+ #
+
+ # Read the header.
+ HEADER = array.array('B', 'AFAR\n\000\000\000\001')
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, len(HEADER))
+ if buf != HEADER:
+ Trace('Header does not match: "%s"' % buf)
+ return False
+
+ # Read the file entries.
+ FILE_START = array.array('B', 'FILE')
+ FILE_END = array.array('B', 'file')
+ END = array.array('B', 'END\000')
+ while True:
+ # Entry magic.
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, 4)
+ if buf == FILE_START:
+ # Name length (4 bytes, big endian)
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, 4)
+ (name_len,) = struct.unpack('>I', buf)
+ #Trace('name len %d' % name_len)
+
+ # Name, NUL-terminated.
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, name_len)
+ buf.pop() # Remove trailing NUL byte.
+ file_name = buf.tostring()
+ Trace('FILE: %s' % file_name)
+
+ # File length (4 bytes, big endian)
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, 4)
+ (file_len,) = struct.unpack('>I', buf)
+
+ # File data.
+ data = array.array('B')
+ data.fromfile(ep.stdout, file_len)
+ #Trace('FILE: read %d bytes from %s' % (file_len, file_name))
+
+ # adler32 (4 bytes, big endian)
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, 4)
+ (adler32,) = struct.unpack('>i', buf) # adler32 wants a signed int ('i')
+ data_adler32 = zlib.adler32(data)
+ # Because of a difference in behavior of zlib.adler32 on 32-bit and 64-bit
+ # systems (one returns signed values, the other unsigned), we take the
+ # modulo 2**32 of the checksums, and compare those.
+ # See also http://bugs.python.org/issue1202
+ if (adler32 % (2**32)) != (data_adler32 % (2**32)):
+ Trace('adler32 does not match: calculated 0x%08x != expected 0x%08x' %
+ (data_adler32, adler32))
+ return False
+
+ # File end magic.
+ buf = array.array('B')
+ buf.fromfile(ep.stdout, 4)
+ if buf != FILE_END:
+ Trace('Unexpected file end magic "%s"' % buf)
+ return False
+
+ # Write to the output file
+ out_file_name = dest + '/' + file_name[len(src):]
+ p = os.path.dirname(out_file_name)
+ if not os.path.exists(p): os.makedirs(p)
+ fo = file(out_file_name, 'w+b')
+ fo.truncate(0)
+ Trace('FILE: Writing %d bytes to %s' % (len(data), out_file_name))
+ data.tofile(fo)
+ fo.close()
+
+ elif buf == END:
+ break
+ else:
+ Trace('Unexpected magic "%s"' % buf)
+ return False
+
+ return WaitForPrompt(ep, timeout=timeout, reset_on_activity=True)
+
+
+def ReadBootClassPath(ep, timeout=0):
+ """Reads and returns the default bootclasspath as a list of files.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ timeout: The number of seconds to wait for the command to complete,
+ or zero for no timeout.
+
+ Returns:
+ The bootclasspath as a list of strings.
+ None on failure.
+ """
+ bcp = RunEmulatorCommand(ep, 'echo $BOOTCLASSPATH', timeout=timeout)
+ if not bcp:
+ Trace('Could not find bootclasspath')
+ return None
+ return bcp.strip().split(':') # strip trailing newline
+
+
+def RunDexoptOnFileList(ep, files, dest_root, move=False, timeout=0):
+ """Creates the corresponding .odex file for all jar/apk files in 'files'.
+ Copies the .odex file to a location under 'dest_root'. If 'move' is True,
+ the file is moved instead of copied.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ files: The list of files to optimize
+ dest_root: directory to copy/move odex files to. Must already exist.
+ move: if True, move rather than copy files
+ timeout: The number of seconds to wait for the command to complete,
+ or zero for no timeout.
+
+ Returns:
+ True on success, False on failure.
+ """
+ for jar_file in files:
+ if jar_file.endswith('.apk') or jar_file.endswith('.jar'):
+ odex_file = jar_file[:jar_file.rfind('.')] + '.odex'
+ cmd = 'dexopt-wrapper %s %s' % (jar_file, odex_file)
+ if not RunEmulatorCommand(ep, cmd, timeout=timeout):
+ Trace('"%s" failed' % cmd)
+ return False
+
+ # Always copy the odex file. There's no cp(1), so we
+ # cat out to the new file.
+ dst_odex = dest_root + odex_file
+ cmd = 'cat %s > %s' % (odex_file, dst_odex) # no cp(1)
+ if not RunEmulatorCommand(ep, cmd, timeout=timeout):
+ Trace('"%s" failed' % cmd)
+ return False
+
+ # Move it if we're asked to. We can't use mv(1) because
+ # the files tend to move between filesystems.
+ if move:
+ cmd = 'rm %s' % odex_file
+ if not RunEmulatorCommand(ep, cmd, timeout=timeout):
+ Trace('"%s" failed' % cmd)
+ return False
+ return True
+
+
+def InstallCacheFiles(cache_system_dir, out_system_dir):
+ """Install files in cache_system_dir to the proper places in out_system_dir.
+
+ cache_system_dir contains various files from /system, plus .odex files
+ for most of the .apk/.jar files that live there.
+ This function copies each .odex file from the cache dir to the output dir
+ and removes "classes.dex" from each appropriate .jar/.apk.
+
+ E.g., <cache_system_dir>/app/NotePad.odex would be copied to
+ <out_system_dir>/app/NotePad.odex, and <out_system_dir>/app/NotePad.apk
+ would have its classes.dex file removed.
+
+ Args:
+ cache_system_dir: The directory containing the cache files scraped from
+ the emulator.
+ out_system_dir: The local directory that corresponds to "/system"
+ on the device filesystem. (the root of system.img)
+
+ Returns:
+ True if everything succeeded, False if any problems occurred.
+ """
+ # First, walk through cache_system_dir and copy every .odex file
+ # over to out_system_dir, ensuring that the destination directory
+ # contains the corresponding source file.
+ for root, dirs, files in os.walk(cache_system_dir):
+ for name in files:
+ if name.endswith('.odex'):
+ odex_file = os.path.join(root, name)
+
+ # Find the path to the .odex file's source apk/jar file.
+ out_stem = odex_file[len(cache_system_dir):odex_file.rfind('.')]
+ out_stem = out_system_dir + out_stem;
+ jar_file = out_stem + '.jar'
+ if not os.path.exists(jar_file):
+ jar_file = out_stem + '.apk'
+ if not os.path.exists(jar_file):
+ Trace('Cannot find source .jar/.apk for %s: %s' %
+ (odex_file, out_stem + '.{jar,apk}'))
+ return False
+
+ # Copy the cache file next to the source file.
+ cmd = ['cp', odex_file, out_stem + '.odex']
+ ret = subprocess.call(cmd)
+ if ret: # non-zero exit status
+ Trace('%s failed' % ' '.join(cmd))
+ return False
+
+ # Walk through the output /system directory, making sure
+ # that every .jar/.apk has an odex file. While we do this,
+ # remove the classes.dex entry from each source archive.
+ for root, dirs, files in os.walk(out_system_dir):
+ for name in files:
+ if name.endswith('.apk') or name.endswith('.jar'):
+ jar_file = os.path.join(root, name)
+ odex_file = jar_file[:jar_file.rfind('.')] + '.odex'
+ if not os.path.exists(odex_file):
+ if root.endswith('/system/app') or root.endswith('/system/framework'):
+ Trace('jar/apk %s has no .odex file %s' % (jar_file, odex_file))
+ return False
+ else:
+ continue
+
+ # Attempting to dexopt a jar with no classes.dex currently
+ # creates a 40-byte odex file.
+ # TODO: use a more reliable check
+ if os.path.getsize(odex_file) > 100:
+ # Remove classes.dex from the .jar file.
+ cmd = ['zip', '-dq', jar_file, 'classes.dex']
+ ret = subprocess.call(cmd)
+ if ret: # non-zero exit status
+ Trace('"%s" failed' % ' '.join(cmd))
+ return False
+ else:
+ # Some of the apk files don't contain any code.
+ if not name.endswith('.apk'):
+ Trace('%s has a zero-length odex file' % jar_file)
+ return False
+ cmd = ['rm', odex_file]
+ ret = subprocess.call(cmd)
+ if ret: # non-zero exit status
+ Trace('"%s" failed' % ' '.join(cmd))
+ return False
+
+ return True
+
+
+def KillChildProcess(p, sig=signal.SIGTERM, timeout=0):
+ """Waits for a child process to die without getting stuck in wait().
+
+ After Jean Brouwers's 2004 post to python-list.
+
+ Args:
+ p: A subprocess.Popen representing the child process to kill.
+ sig: The signal to send to the child process.
+ timeout: How many seconds to wait for the child process to die.
+ If zero, do not time out.
+
+ Returns:
+ The exit status of the child process, if it was successfully killed.
+ The final value of p.returncode if it wasn't.
+ """
+ os.kill(p.pid, sig)
+ if timeout > 0:
+ while p.poll() < 0:
+ if timeout > 0.5:
+ timeout -= 0.25
+ time.sleep(0.25)
+ else:
+ os.kill(p.pid, signal.SIGKILL)
+ time.sleep(0.5)
+ p.poll()
+ break
+ else:
+ p.wait()
+ return p.returncode
+
+
+def Trace(msg):
+ """Prints a message to stdout.
+
+ Args:
+ msg: The message to print.
+ """
+ #print 'dexpreopt: %s' % msg
+ when = datetime.datetime.now()
+ print '%02d:%02d.%d dexpreopt: %s' % (when.minute, when.second, when.microsecond, msg)
+
+
+def KillEmulator():
+ """Attempts to kill the emulator process, if it is running.
+
+ Returns:
+ The exit status of the emulator process, or None if the emulator
+ was not running or was unable to be killed.
+ """
+ global _emulator_popen
+ if _emulator_popen:
+ Trace('Killing emulator')
+ try:
+ ret = KillChildProcess(_emulator_popen, sig=signal.SIGINT, timeout=5)
+ except OSError:
+ Trace('Could not kill emulator')
+ ret = None
+ _emulator_popen = None
+ return ret
+ return None
+
+
+def Fail(msg=None):
+ """Prints an error and causes the process to exit.
+
+ Args:
+ msg: Additional error string to print (optional).
+
+ Returns:
+ Does not return.
+ """
+ s = 'dexpreopt: ERROR'
+ if msg: s += ': %s' % msg
+ print >>sys.stderr, msg
+ KillEmulator()
+ sys.exit(1)
+
+
+def PrintUsage(msg=None):
+ """Prints commandline usage information for the tool and exits with an error.
+
+ Args:
+ msg: Additional string to print (optional).
+
+ Returns:
+ Does not return.
+ """
+ if msg:
+ print >>sys.stderr, 'dexpreopt: %s', msg
+ print >>sys.stderr, """Usage: dexpreopt <options>
+Required options:
+ -kernel <kernel file> Kernel to use when running the emulator
+ -ramdisk <ramdisk.img file> Ramdisk to use when running the emulator
+ -image <system.img file> System image to use when running the
+ emulator. /system/app should contain the
+ .apk files to optimize, and any required
+ bootclasspath libraries must be present
+ in the correct locations.
+ -system <path> The product directory, which usually contains
+ files like 'system.img' (files other than
+ the kernel in that directory won't
+ be used)
+ -outsystemdir <path> A fully-populated /system directory, ready
+ to be modified to contain the optimized
+ files. The appropriate .jar/.apk files
+ will be stripped of their classes.dex
+ entries, and the optimized .dex files
+ will be added alongside the packages
+ that they came from.
+Optional:
+ -tmpdir <path> If specified, use this directory for
+ intermediate objects. If not specified,
+ a unique directory under the system
+ temp dir is used.
+ """
+ sys.exit(2)
+
+
+def ParseArgs(argv):
+ """Parses commandline arguments.
+
+ Args:
+ argv: A list of arguments; typically sys.argv[1:]
+
+ Returns:
+ A tuple containing two dictionaries; the first contains arguments
+ that will be passsed to the emulator, and the second contains other
+ arguments.
+ """
+ parser = optparse.OptionParser()
+
+ parser.add_option('--kernel', help='Passed to emulator')
+ parser.add_option('--ramdisk', help='Passed to emulator')
+ parser.add_option('--image', help='Passed to emulator')
+ parser.add_option('--system', help='Passed to emulator')
+ parser.add_option('--outsystemdir', help='Destination /system directory')
+ parser.add_option('--tmpdir', help='Optional temp directory to use')
+
+ options, args = parser.parse_args(args=argv)
+ if args: PrintUsage()
+
+ emulator_args = {}
+ other_args = {}
+ if options.kernel: emulator_args['kernel'] = options.kernel
+ if options.ramdisk: emulator_args['ramdisk'] = options.ramdisk
+ if options.image: emulator_args['image'] = options.image
+ if options.system: emulator_args['system'] = options.system
+ if options.outsystemdir: other_args['outsystemdir'] = options.outsystemdir
+ if options.tmpdir: other_args['tmpdir'] = options.tmpdir
+
+ return (emulator_args, other_args)
+
+
+def DexoptEverything(ep, dest_root):
+ """Logic for finding and dexopting files in the necessary order.
+
+ Args:
+ ep: A subprocess.Popen object referring to the emulator process.
+ dest_root: directory to copy/move odex files to
+
+ Returns:
+ True on success, False on failure.
+ """
+ _extra_tests = False
+ if _extra_tests:
+ if not RunEmulatorCommand(ep, 'ls /system/app', timeout=5):
+ Fail('Could not ls')
+
+ # We're very short on space, so remove a bunch of big stuff that we
+ # don't need.
+ cmd = 'rm -r /system/sounds /system/media /system/fonts /system/xbin'
+ if not RunEmulatorCommand(ep, cmd, timeout=40):
+ Trace('"%s" failed' % cmd)
+ return False
+
+ Trace('Read file list')
+ jar_dirs = ['/system/framework', '/system/app']
+ files = ReadFileList(ep, jar_dirs, timeout=5)
+ if not files:
+ Fail('Could not list files in %s' % ' '.join(jar_dirs))
+ #Trace('File list:\n"""\n%s\n"""' % '\n'.join(files))
+
+ bcp = ReadBootClassPath(ep, timeout=2)
+ if not files:
+ Fail('Could not sort by bootclasspath')
+
+ # Remove bootclasspath entries from the main file list.
+ for jar in bcp:
+ try:
+ files.remove(jar)
+ except ValueError:
+ Trace('File list does not contain bootclasspath entry "%s"' % jar)
+ return False
+
+ # Create the destination directories.
+ for d in ['', '/system'] + jar_dirs:
+ cmd = 'mkdir %s%s' % (dest_root, d)
+ if not RunEmulatorCommand(ep, cmd, timeout=4):
+ Trace('"%s" failed' % cmd)
+ return False
+
+ # First, dexopt the bootclasspath. Keep their cache files in place.
+ Trace('Dexopt %d bootclasspath files' % len(bcp))
+ if not RunDexoptOnFileList(ep, bcp, dest_root, timeout=120):
+ Trace('Could not dexopt bootclasspath')
+ return False
+
+ # dexopt the rest. To avoid running out of space on the emulator
+ # volume, move each cache file after it's been created.
+ Trace('Dexopt %d files' % len(files))
+ if not RunDexoptOnFileList(ep, files, dest_root, move=True, timeout=120):
+ Trace('Could not dexopt files')
+ return False
+
+ if _extra_tests:
+ if not RunEmulatorCommand(ep, 'ls /system/app', timeout=5):
+ Fail('Could not ls')
+
+ return True
+
+
+
+def MainInternal():
+ """Main function that can be wrapped in a try block.
+
+ Returns:
+ Nothing.
+ """
+ emulator_args, other_args = ParseArgs(sys.argv[1:])
+
+ tmp_dir = EnsureTempDir(other_args.get('tmpdir'))
+ if not tmp_dir: Fail('Could not create temp dir')
+
+ Trace('Creating data image')
+ userdata = '%s/data.img' % tmp_dir
+ if not CreateZeroedFile(userdata, 32 * 1024 * 1024):
+ Fail('Could not create data image file')
+ emulator_args['userdata'] = userdata
+
+ ep = StartEmulator(**emulator_args)
+ if not ep: Fail('Could not start emulator')
+ global _emulator_popen
+ _emulator_popen = ep
+
+ # TODO: unlink the big userdata file now, since the emulator
+ # has it open.
+
+ if not WaitForEmulator(ep, timeout=20): Fail('Emulator did not respond')
+ if not ReplaceEmulatorPrompt(ep): Fail('Could not replace prompt')
+
+ dest_root = '/data/dexpreopt-root'
+ if not DexoptEverything(ep, dest_root): Fail('Could not dexopt files')
+
+ # Grab the odex files that were left in dest_root.
+ cache_system_dir = tmp_dir + '/cache-system'
+ if not DownloadDirectoryHierarchy(ep, dest_root + '/system',
+ cache_system_dir,
+ timeout=20):
+ Fail('Could not download %s/system from emulator' % dest_root)
+
+ if not InstallCacheFiles(cache_system_dir=cache_system_dir,
+ out_system_dir=other_args['outsystemdir']):
+ Fail('Could not install files')
+
+ Trace('dexpreopt successful')
+ # Success!
+
+
+def main():
+ try:
+ MainInternal()
+ finally:
+ KillEmulator()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/dexpreopt/geninitrc.awk b/tools/dexpreopt/geninitrc.awk
new file mode 100644
index 000000000..4b67e78e0
--- /dev/null
+++ b/tools/dexpreopt/geninitrc.awk
@@ -0,0 +1,62 @@
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+BEGIN {
+ fixed_remount = 0;
+ console_state = 0;
+}
+
+/^ mount yaffs2 mtd@system \/system ro remount$/ {
+ fixed_remount = 1;
+ print " # dexpreopt needs to write to /system";
+ print " ### " $0;
+ next;
+}
+
+console_state == 0 && /^service console \/system\/bin\/sh$/ {
+ console_state = 1;
+ print;
+ next;
+}
+
+console_state == 1 && /^ console$/ {
+ console_state = 2;
+ print;
+ exit;
+}
+
+console_state == 1 {
+ # The second line of the console entry should always immediately
+ # follow the first.
+ exit;
+}
+
+{ print }
+
+END {
+ failed = 0;
+ if (fixed_remount != 1) {
+ print "ERROR: no match for remount line" > "/dev/stderr";
+ failed = 1;
+ }
+ if (console_state != 2) {
+ print "ERROR: no match for console lines" > "/dev/stderr";
+ failed = 1;
+ }
+ if (failed == 1) {
+ print ">>>> FAILED <<<<"
+ exit 1;
+ }
+}
diff --git a/tools/droiddoc/Android.mk b/tools/droiddoc/Android.mk
new file mode 100644
index 000000000..d2d7a95b0
--- /dev/null
+++ b/tools/droiddoc/Android.mk
@@ -0,0 +1,18 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(LOCAL_PATH)/src/Android.mk
+
diff --git a/tools/droiddoc/src/Android.mk b/tools/droiddoc/src/Android.mk
new file mode 100644
index 000000000..abd258151
--- /dev/null
+++ b/tools/droiddoc/src/Android.mk
@@ -0,0 +1,69 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := docs
+
+LOCAL_SRC_FILES := \
+ AnnotationInstanceInfo.java \
+ AnnotationValueInfo.java \
+ AttributeInfo.java \
+ AttrTagInfo.java \
+ ClassInfo.java \
+ DroidDoc.java \
+ ClearPage.java \
+ Comment.java \
+ ContainerInfo.java \
+ Converter.java \
+ DocFile.java \
+ DocInfo.java \
+ Errors.java \
+ FieldInfo.java \
+ Hierarchy.java \
+ InheritedTags.java \
+ KeywordEntry.java \
+ LinkReference.java \
+ LiteralTagInfo.java \
+ MemberInfo.java \
+ MethodInfo.java \
+ NavTree.java \
+ PackageInfo.java \
+ ParamTagInfo.java \
+ ParameterInfo.java \
+ ParsedTagInfo.java \
+ Proofread.java \
+ SampleCode.java \
+ SampleTagInfo.java \
+ Scoped.java \
+ SeeTagInfo.java \
+ Sorter.java \
+ SourcePositionInfo.java \
+ Stubs.java \
+ TagInfo.java \
+ TextTagInfo.java \
+ ThrowsTagInfo.java \
+ TodoFile.java \
+ TypeInfo.java
+
+LOCAL_JAVA_LIBRARIES := \
+ clearsilver
+
+LOCAL_CLASSPATH := \
+ $(HOST_JDK_TOOLS_JAR)
+
+LOCAL_MODULE:= droiddoc
+
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/droiddoc/src/AnnotationInstanceInfo.java b/tools/droiddoc/src/AnnotationInstanceInfo.java
new file mode 100644
index 000000000..07d4aa3eb
--- /dev/null
+++ b/tools/droiddoc/src/AnnotationInstanceInfo.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class AnnotationInstanceInfo
+{
+ private ClassInfo mType;
+ private AnnotationValueInfo[] mElementValues;
+
+ public AnnotationInstanceInfo(ClassInfo type, AnnotationValueInfo[] elementValues)
+ {
+ mType = type;
+ mElementValues = elementValues;
+ }
+
+ ClassInfo type()
+ {
+ return mType;
+ }
+
+ AnnotationValueInfo[] elementValues()
+ {
+ return mElementValues;
+ }
+
+ public String toString()
+ {
+ StringBuilder str = new StringBuilder();
+ str.append("@");
+ str.append(mType.qualifiedName());
+ str.append("(");
+ AnnotationValueInfo[] values = mElementValues;
+ final int N = values.length;
+ for (int i=0; i<N; i++) {
+ AnnotationValueInfo value = values[i];
+ str.append(value.element().name());
+ str.append("=");
+ str.append(value.valueString());
+ if (i != N-1) {
+ str.append(",");
+ }
+ }
+ str.append(")");
+ return str.toString();
+ }
+}
+
diff --git a/tools/droiddoc/src/AnnotationValueInfo.java b/tools/droiddoc/src/AnnotationValueInfo.java
new file mode 100644
index 000000000..a2d869a5a
--- /dev/null
+++ b/tools/droiddoc/src/AnnotationValueInfo.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class AnnotationValueInfo
+{
+ private Object mValue;
+ private String mString;
+ private MethodInfo mElement;
+
+ public AnnotationValueInfo(MethodInfo element)
+ {
+ mElement = element;
+ }
+
+ public void init(Object value)
+ {
+ mValue = value;
+ }
+
+ public MethodInfo element()
+ {
+ return mElement;
+ }
+
+ public Object value()
+ {
+ return mValue;
+ }
+
+ public String valueString()
+ {
+ Object v = mValue;
+ if (v instanceof TypeInfo) {
+ return ((TypeInfo)v).fullName();
+ }
+ else if (v instanceof FieldInfo) {
+ StringBuilder str = new StringBuilder();
+ FieldInfo f = (FieldInfo)v;
+ str.append(f.containingClass().qualifiedName());
+ str.append('.');
+ str.append(f.name());
+ return str.toString();
+ }
+ else if (v instanceof AnnotationInstanceInfo) {
+ return v.toString();
+ }
+ else if (v instanceof AnnotationValueInfo[]) {
+ StringBuilder str = new StringBuilder();
+ AnnotationValueInfo[] array = (AnnotationValueInfo[])v;
+ final int N = array.length;
+ str.append("{");
+ for (int i=0; i<array.length; i++) {
+ str.append(array[i].valueString());
+ if (i != N-1) {
+ str.append(",");
+ }
+ }
+ str.append("}");
+ return str.toString();
+ }
+ else {
+ return FieldInfo.constantLiteralValue(v);
+ }
+ }
+}
+
diff --git a/tools/droiddoc/src/AttrTagInfo.java b/tools/droiddoc/src/AttrTagInfo.java
new file mode 100644
index 000000000..abc545263
--- /dev/null
+++ b/tools/droiddoc/src/AttrTagInfo.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+
+public class AttrTagInfo extends TagInfo
+{
+ private static final String REF_COMMAND = "ref";
+ private static final String NAME_COMMAND = "name";
+ private static final String DESCRIPTION_COMMAND = "description";
+ private static final Pattern TEXT = Pattern.compile("(\\S+)\\s*(.*)", Pattern.DOTALL);
+ private static final Pattern NAME_TEXT = Pattern.compile("(\\S+)(.*)",
+ Pattern.DOTALL);
+
+ private ContainerInfo mBase;
+ private String mCommand;
+
+ // if mCommand == "ref"
+ private FieldInfo mRefField;
+ private AttributeInfo mAttrInfo;
+
+ // if mCommand == "name"
+ private String mAttrName;
+
+ // if mCommand == "description"
+ private Comment mDescrComment;
+
+ AttrTagInfo(String name, String kind, String text, ContainerInfo base,
+ SourcePositionInfo position)
+ {
+ super(name, kind, text, position);
+ mBase = base;
+
+ parse(text, base, position);
+ }
+
+ void parse(String text, ContainerInfo base, SourcePositionInfo position) {
+ Matcher m;
+
+ m = TEXT.matcher(text);
+ if (!m.matches()) {
+ Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr tag: " + text);
+ return;
+ }
+
+ String command = m.group(1);
+ String more = m.group(2);
+
+ if (REF_COMMAND.equals(command)) {
+ String ref = more.trim();
+ LinkReference linkRef = LinkReference.parse(ref, mBase, position, false);
+ if (!linkRef.good) {
+ Errors.error(Errors.BAD_ATTR_TAG, position, "Unresolved @attr ref: " + ref);
+ return;
+ }
+ if (!(linkRef.memberInfo instanceof FieldInfo)) {
+ Errors.error(Errors.BAD_ATTR_TAG, position, "@attr must be a field: " + ref);
+ return;
+ }
+ mCommand = command;
+ mRefField = (FieldInfo)linkRef.memberInfo;
+ }
+ else if (NAME_COMMAND.equals(command)) {
+ m = NAME_TEXT.matcher(more);
+ if (!m.matches() || m.group(2).trim().length() != 0) {
+ Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr name tag: " + more);
+ return;
+ }
+ mCommand = command;
+ mAttrName = m.group(1);
+ }
+ else if (DESCRIPTION_COMMAND.equals(command)) {
+ mCommand = command;
+ mDescrComment = new Comment(more, base, position);
+ }
+ else {
+ Errors.error(Errors.BAD_ATTR_TAG, position, "Bad @attr command: " + command);
+ }
+ }
+
+ public FieldInfo reference() {
+ return REF_COMMAND.equals(mCommand) ? mRefField : null;
+ }
+
+ public String name() {
+ return NAME_COMMAND.equals(mCommand) ? mAttrName : null;
+ }
+
+ public Comment description() {
+ return DESCRIPTION_COMMAND.equals(mCommand) ? mDescrComment : null;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ super.makeHDF(data, base);
+ }
+
+ public void setAttribute(AttributeInfo info) {
+ mAttrInfo = info;
+ }
+
+ public static void makeReferenceHDF(HDF data, String base, AttrTagInfo[] tags)
+ {
+ int i=0;
+ for (AttrTagInfo t: tags) {
+ if (REF_COMMAND.equals(t.mCommand)) {
+ if (t.mAttrInfo == null) {
+ String msg = "ERROR: unlinked attr: " + t.mRefField.name();
+ if (false) {
+ System.out.println(msg);
+ } else {
+ throw new RuntimeException(msg);
+ }
+ } else {
+ data.setValue(base + "." + i + ".name", t.mAttrInfo.name());
+ data.setValue(base + "." + i + ".href", t.mAttrInfo.htmlPage());
+ i++;
+ }
+ }
+ }
+ }
+
+}
diff --git a/tools/droiddoc/src/AttributeInfo.java b/tools/droiddoc/src/AttributeInfo.java
new file mode 100644
index 000000000..a24106bb6
--- /dev/null
+++ b/tools/droiddoc/src/AttributeInfo.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Comparator;
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+public class AttributeInfo {
+ public static final Comparator<AttributeInfo> comparator = new Comparator<AttributeInfo>() {
+ public int compare(AttributeInfo a, AttributeInfo b) {
+ return a.name().compareTo(b.name());
+ }
+ };
+
+ public FieldInfo attrField;
+ public ArrayList<MethodInfo> methods = new ArrayList<MethodInfo>();
+
+ private ClassInfo mClass;
+ private String mName;
+ private Comment mComment;
+
+ public AttributeInfo(ClassInfo cl, FieldInfo f) {
+ mClass = cl;
+ attrField = f;
+ }
+
+ public String name() {
+ if (mName == null) {
+ for (AttrTagInfo comment: attrField.comment().attrTags()) {
+ String n = comment.name();
+ if (n != null) {
+ mName = n;
+ return n;
+ }
+ }
+ }
+ return mName;
+ }
+
+ public Comment comment() {
+ if (mComment == null) {
+ for (AttrTagInfo attr: attrField.comment().attrTags()) {
+ Comment c = attr.description();
+ if (c != null) {
+ mComment = c;
+ return c;
+ }
+ }
+ }
+ if (mComment == null) {
+ return new Comment("", mClass, new SourcePositionInfo());
+ }
+ return mComment;
+ }
+
+ public String anchor() {
+ return "attr_" + name();
+ }
+ public String htmlPage() {
+ return mClass.htmlPage() + "#" + anchor();
+ }
+
+ public void makeHDF(HDF data, String base) {
+ data.setValue(base + ".name", name());
+ data.setValue(base + ".anchor", anchor());
+ data.setValue(base + ".href", htmlPage());
+ data.setValue(base + ".R.name", attrField.name());
+ data.setValue(base + ".R.href", attrField.htmlPage());
+ TagInfo.makeHDF(data, base + ".deprecated", attrField.comment().deprecatedTags());
+ TagInfo.makeHDF(data, base + ".shortDescr", comment().briefTags());
+ TagInfo.makeHDF(data, base + ".descr", comment().tags());
+
+ int i=0;
+ for (MethodInfo m: methods) {
+ String s = base + ".methods." + i;
+ data.setValue(s + ".href", m.htmlPage());
+ data.setValue(s + ".name", m.name() + m.prettySignature());
+ }
+ }
+
+ public boolean checkLevel() {
+ return attrField.checkLevel();
+ }
+}
+
diff --git a/tools/droiddoc/src/ClassInfo.java b/tools/droiddoc/src/ClassInfo.java
new file mode 100644
index 000000000..36edbf801
--- /dev/null
+++ b/tools/droiddoc/src/ClassInfo.java
@@ -0,0 +1,1463 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class ClassInfo extends DocInfo implements ContainerInfo, Comparable, Scoped
+{
+ public static final Comparator<ClassInfo> comparator = new Comparator<ClassInfo>() {
+ public int compare(ClassInfo a, ClassInfo b) {
+ return a.name().compareTo(b.name());
+ }
+ };
+
+ public static final Comparator<ClassInfo> qualifiedComparator = new Comparator<ClassInfo>() {
+ public int compare(ClassInfo a, ClassInfo b) {
+ return a.qualifiedName().compareTo(b.qualifiedName());
+ }
+ };
+
+ public ClassInfo(
+ ClassDoc cl,
+ String rawCommentText, SourcePositionInfo position,
+ boolean isPublic, boolean isProtected, boolean isPackagePrivate,
+ boolean isPrivate, boolean isStatic,
+ boolean isInterface, boolean isAbstract, boolean isOrdinaryClass,
+ boolean isException, boolean isError, boolean isEnum, boolean isAnnotation,
+ boolean isFinal, boolean isIncluded, String name,
+ String qualifiedName, String qualifiedTypeName, boolean isPrimitive)
+ {
+ super(rawCommentText, position);
+
+ mClass = cl;
+ mIsPublic = isPublic;
+ mIsProtected = isProtected;
+ mIsPackagePrivate = isPackagePrivate;
+ mIsPrivate = isPrivate;
+ mIsStatic = isStatic;
+ mIsInterface = isInterface;
+ mIsAbstract = isAbstract;
+ mIsOrdinaryClass = isOrdinaryClass;
+ mIsException = isException;
+ mIsError = isError;
+ mIsEnum = isEnum;
+ mIsAnnotation = isAnnotation;
+ mIsFinal = isFinal;
+ mIsIncluded = isIncluded;
+ mName = name;
+ mQualifiedName = qualifiedName;
+ mQualifiedTypeName = qualifiedTypeName;
+ mIsPrimitive = isPrimitive;
+ mNameParts = name.split("\\.");
+ }
+
+ public void init(TypeInfo typeInfo, ClassInfo[] interfaces, TypeInfo[] interfaceTypes,
+ ClassInfo[] innerClasses,
+ MethodInfo[] constructors, MethodInfo[] methods, MethodInfo[] annotationElements,
+ FieldInfo[] fields, FieldInfo[] enumConstants,
+ PackageInfo containingPackage, ClassInfo containingClass,
+ ClassInfo superclass, TypeInfo superclassType, AnnotationInstanceInfo[] annotations)
+ {
+ mTypeInfo = typeInfo;
+ mRealInterfaces = interfaces;
+ mRealInterfaceTypes = interfaceTypes;
+ mInnerClasses = innerClasses;
+ mAllConstructors = constructors;
+ mAllSelfMethods = methods;
+ mAnnotationElements = annotationElements;
+ mAllSelfFields = fields;
+ mEnumConstants = enumConstants;
+ mContainingPackage = containingPackage;
+ mContainingClass = containingClass;
+ mRealSuperclass = superclass;
+ mRealSuperclassType = superclassType;
+ mAnnotations = annotations;
+
+ // after providing new methods and new superclass info,clear any cached
+ // lists of self + superclass methods, ctors, etc.
+ mSuperclassInit = false;
+ mConstructors = null;
+ mMethods = null;
+ mSelfMethods = null;
+ mFields = null;
+ mSelfFields = null;
+ mSelfAttributes = null;
+ mDeprecatedKnown = false;
+
+ Arrays.sort(mEnumConstants, FieldInfo.comparator);
+ Arrays.sort(mInnerClasses, ClassInfo.comparator);
+ }
+
+ public void init2() {
+ // calling this here forces the AttrTagInfo objects to be linked to the AttribtueInfo
+ // objects
+ selfAttributes();
+ }
+
+ public void init3(TypeInfo[] types, ClassInfo[] realInnerClasses){
+ mTypeParameters = types;
+ mRealInnerClasses = realInnerClasses;
+ }
+
+ public ClassInfo[] getRealInnerClasses(){
+ return mRealInnerClasses;
+ }
+
+ public TypeInfo[] getTypeParameters(){
+ return mTypeParameters;
+ }
+
+ public boolean checkLevel()
+ {
+ int val = mCheckLevel;
+ if (val >= 0) {
+ return val != 0;
+ } else {
+ boolean v = DroidDoc.checkLevel(mIsPublic, mIsProtected,
+ mIsPackagePrivate, mIsPrivate, isHidden());
+ mCheckLevel = v ? 1 : 0;
+ return v;
+ }
+ }
+
+ public int compareTo(Object that) {
+ if (that instanceof ClassInfo) {
+ return mQualifiedName.compareTo(((ClassInfo)that).mQualifiedName);
+ } else {
+ return this.hashCode() - that.hashCode();
+ }
+ }
+
+ public ContainerInfo parent()
+ {
+ return this;
+ }
+
+ public boolean isPublic()
+ {
+ return mIsPublic;
+ }
+
+ public boolean isProtected()
+ {
+ return mIsProtected;
+ }
+
+ public boolean isPackagePrivate()
+ {
+ return mIsPackagePrivate;
+ }
+
+ public boolean isPrivate()
+ {
+ return mIsPrivate;
+ }
+
+ public boolean isStatic()
+ {
+ return mIsStatic;
+ }
+
+ public boolean isInterface()
+ {
+ return mIsInterface;
+ }
+
+ public boolean isAbstract()
+ {
+ return mIsAbstract;
+ }
+
+ public PackageInfo containingPackage()
+ {
+ return mContainingPackage;
+ }
+
+ public ClassInfo containingClass()
+ {
+ return mContainingClass;
+ }
+
+ public boolean isOrdinaryClass()
+ {
+ return mIsOrdinaryClass;
+ }
+
+ public boolean isException()
+ {
+ return mIsException;
+ }
+
+ public boolean isError()
+ {
+ return mIsError;
+ }
+
+ public boolean isEnum()
+ {
+ return mIsEnum;
+ }
+
+ public boolean isAnnotation()
+ {
+ return mIsAnnotation;
+ }
+
+ public boolean isFinal()
+ {
+ return mIsFinal;
+ }
+
+ public boolean isIncluded()
+ {
+ return mIsIncluded;
+ }
+
+ public HashSet<String> typeVariables()
+ {
+ HashSet<String> result = TypeInfo.typeVariables(mTypeInfo.typeArguments());
+ ClassInfo cl = containingClass();
+ while (cl != null) {
+ TypeInfo[] types = cl.asTypeInfo().typeArguments();
+ if (types != null) {
+ TypeInfo.typeVariables(types, result);
+ }
+ cl = cl.containingClass();
+ }
+ return result;
+ }
+
+ private static void gatherHiddenInterfaces(ClassInfo cl, HashSet<ClassInfo> interfaces) {
+ for (ClassInfo iface: cl.mRealInterfaces) {
+ if (iface.checkLevel()) {
+ interfaces.add(iface);
+ } else {
+ gatherHiddenInterfaces(iface, interfaces);
+ }
+ }
+ }
+
+ public ClassInfo[] interfaces()
+ {
+ if (mInterfaces == null) {
+ if (checkLevel()) {
+ HashSet<ClassInfo> interfaces = new HashSet<ClassInfo>();
+ ClassInfo superclass = mRealSuperclass;
+ while (superclass != null && !superclass.checkLevel()) {
+ gatherHiddenInterfaces(superclass, interfaces);
+ superclass = superclass.mRealSuperclass;
+ }
+ gatherHiddenInterfaces(this, interfaces);
+ mInterfaces = interfaces.toArray(new ClassInfo[interfaces.size()]);
+ } else {
+ // put something here in case someone uses it
+ mInterfaces = mRealInterfaces;
+ }
+ Arrays.sort(mInterfaces, ClassInfo.qualifiedComparator);
+ }
+ return mInterfaces;
+ }
+
+ public ClassInfo[] realInterfaces()
+ {
+ return mRealInterfaces;
+ }
+
+ TypeInfo[] realInterfaceTypes()
+ {
+ return mRealInterfaceTypes;
+ }
+
+ public String name()
+ {
+ return mName;
+ }
+
+ public String[] nameParts()
+ {
+ return mNameParts;
+ }
+
+ public String leafName()
+ {
+ return mNameParts[mNameParts.length-1];
+ }
+
+ public String qualifiedName()
+ {
+ return mQualifiedName;
+ }
+
+ public String qualifiedTypeName()
+ {
+ return mQualifiedTypeName;
+ }
+
+ public boolean isPrimitive()
+ {
+ return mIsPrimitive;
+ }
+
+ public MethodInfo[] allConstructors() {
+ return mAllConstructors;
+ }
+
+ public MethodInfo[] constructors()
+ {
+ if (mConstructors == null) {
+ MethodInfo[] methods = mAllConstructors;
+ ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
+ for (int i=0; i<methods.length; i++) {
+ MethodInfo m = methods[i];
+ if (!m.isHidden()) {
+ ctors.add(m);
+ }
+ }
+ mConstructors = ctors.toArray(new MethodInfo[ctors.size()]);
+ Arrays.sort(mConstructors, MethodInfo.comparator);
+ }
+ return mConstructors;
+ }
+
+ public ClassInfo[] innerClasses()
+ {
+ return mInnerClasses;
+ }
+
+ public TagInfo[] inlineTags()
+ {
+ return comment().tags();
+ }
+
+ public TagInfo[] firstSentenceTags()
+ {
+ return comment().briefTags();
+ }
+
+ public boolean isDeprecated() {
+ boolean deprecated = false;
+ if (!mDeprecatedKnown) {
+ boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+ boolean annotationDeprecated = false;
+ for (AnnotationInstanceInfo annotation : annotations()) {
+ if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+ annotationDeprecated = true;
+ break;
+ }
+ }
+
+ if (commentDeprecated != annotationDeprecated) {
+ Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+ "Class " + qualifiedName()
+ + ": @Deprecated annotation and @deprecated comment do not match");
+ }
+
+ mIsDeprecated = commentDeprecated | annotationDeprecated;
+ mDeprecatedKnown = true;
+ }
+ return mIsDeprecated;
+ }
+
+ public TagInfo[] deprecatedTags()
+ {
+ TagInfo[] result = comment().deprecatedTags();
+ if (result.length == 0) {
+ if (comment().undeprecateTags().length == 0) {
+ if (superclass() != null) {
+ result = superclass().deprecatedTags();
+ }
+ }
+ }
+ // should we also do the interfaces?
+ return result;
+ }
+
+ public MethodInfo[] methods()
+ {
+ if (mMethods == null) {
+ TreeMap<String,MethodInfo> all = new TreeMap<String,MethodInfo>();
+
+ ClassInfo[] ifaces = interfaces();
+ for (ClassInfo iface: ifaces) {
+ if (iface != null) {
+ MethodInfo[] inhereted = iface.methods();
+ for (MethodInfo method: inhereted) {
+ String key = method.name() + method.signature();
+ all.put(key, method);
+ }
+ }
+ }
+
+ ClassInfo superclass = superclass();
+ if (superclass != null) {
+ MethodInfo[] inhereted = superclass.methods();
+ for (MethodInfo method: inhereted) {
+ String key = method.name() + method.signature();
+ all.put(key, method);
+ }
+ }
+
+ MethodInfo[] methods = selfMethods();
+ for (MethodInfo method: methods) {
+ String key = method.name() + method.signature();
+ MethodInfo old = all.put(key, method);
+ }
+
+ mMethods = all.values().toArray(new MethodInfo[all.size()]);
+ }
+ return mMethods;
+ }
+
+ public MethodInfo[] annotationElements()
+ {
+ return mAnnotationElements;
+ }
+
+ public AnnotationInstanceInfo[] annotations()
+ {
+ return mAnnotations;
+ }
+
+ private static void addFields(ClassInfo cl, TreeMap<String,FieldInfo> all)
+ {
+ FieldInfo[] fields = cl.fields();
+ int N = fields.length;
+ for (int i=0; i<N; i++) {
+ FieldInfo f = fields[i];
+ all.put(f.name(), f);
+ }
+ }
+
+ public FieldInfo[] fields()
+ {
+ if (mFields == null) {
+ int N;
+ TreeMap<String,FieldInfo> all = new TreeMap<String,FieldInfo>();
+
+ ClassInfo[] interfaces = interfaces();
+ N = interfaces.length;
+ for (int i=0; i<N; i++) {
+ addFields(interfaces[i], all);
+ }
+
+ ClassInfo superclass = superclass();
+ if (superclass != null) {
+ addFields(superclass, all);
+ }
+
+ FieldInfo[] fields = selfFields();
+ N = fields.length;
+ for (int i=0; i<N; i++) {
+ FieldInfo f = fields[i];
+ if (!f.isHidden()) {
+ String key = f.name();
+ all.put(key, f);
+ }
+ }
+
+ mFields = all.values().toArray(new FieldInfo[0]);
+ }
+ return mFields;
+ }
+
+ public void gatherFields(ClassInfo owner, ClassInfo cl, HashMap<String,FieldInfo> fields) {
+ FieldInfo[] flds = cl.selfFields();
+ for (FieldInfo f: flds) {
+ if (f.checkLevel()) {
+ fields.put(f.name(), f.cloneForClass(owner));
+ }
+ }
+ }
+
+ public FieldInfo[] selfFields()
+ {
+ if (mSelfFields == null) {
+ HashMap<String,FieldInfo> fields = new HashMap<String,FieldInfo>();
+ // our hidden parents
+ if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
+ gatherFields(this, mRealSuperclass, fields);
+ }
+ for (ClassInfo iface: mRealInterfaces) {
+ if (!iface.checkLevel()) {
+ gatherFields(this, iface, fields);
+ }
+ }
+ // mine
+ FieldInfo[] selfFields = mAllSelfFields;
+ for (int i=0; i<selfFields.length; i++) {
+ FieldInfo f = selfFields[i];
+ if (!f.isHidden()) {
+ fields.put(f.name(), f);
+ }
+ }
+ // combine and return in
+ mSelfFields = fields.values().toArray(new FieldInfo[fields.size()]);
+ Arrays.sort(mSelfFields, FieldInfo.comparator);
+ }
+ return mSelfFields;
+ }
+
+ public FieldInfo[] allSelfFields() {
+ return mAllSelfFields;
+ }
+
+ public void gatherMethods(ClassInfo owner, ClassInfo cl, HashMap<String,MethodInfo> methods) {
+ MethodInfo[] meth = cl.selfMethods();
+ for (MethodInfo m: meth) {
+ if (m.checkLevel()) {
+ methods.put(m.name()+m.signature(), m.cloneForClass(owner));
+ }
+ }
+ }
+
+ public MethodInfo[] selfMethods()
+ {
+ if (mSelfMethods == null) {
+ HashMap<String,MethodInfo> methods = new HashMap<String,MethodInfo>();
+ // our hidden parents
+ if (mRealSuperclass != null && !mRealSuperclass.checkLevel()) {
+ gatherMethods(this, mRealSuperclass, methods);
+ }
+ for (ClassInfo iface: mRealInterfaces) {
+ if (!iface.checkLevel()) {
+ gatherMethods(this, iface, methods);
+ }
+ }
+ // mine
+ MethodInfo[] selfMethods = mAllSelfMethods;
+ for (int i=0; i<selfMethods.length; i++) {
+ MethodInfo m = selfMethods[i];
+ if (m.checkLevel()) {
+ methods.put(m.name()+m.signature(), m);
+ }
+ }
+ // combine and return it
+ mSelfMethods = methods.values().toArray(new MethodInfo[methods.size()]);
+ Arrays.sort(mSelfMethods, MethodInfo.comparator);
+ }
+ return mSelfMethods;
+ }
+
+ public MethodInfo[] allSelfMethods() {
+ return mAllSelfMethods;
+ }
+
+ public void addMethod(MethodInfo method) {
+ MethodInfo[] methods = new MethodInfo[mAllSelfMethods.length + 1];
+ int i = 0;
+ for (MethodInfo m : mAllSelfMethods) {
+ methods[i] = m;
+ i++;
+ }
+ methods[i] = method;
+ mAllSelfMethods = methods;
+ }
+
+ public AttributeInfo[] selfAttributes()
+ {
+ if (mSelfAttributes == null) {
+ TreeMap<FieldInfo,AttributeInfo> attrs = new TreeMap<FieldInfo,AttributeInfo>();
+
+ // the ones in the class comment won't have any methods
+ for (AttrTagInfo tag: comment().attrTags()) {
+ FieldInfo field = tag.reference();
+ if (field != null) {
+ AttributeInfo attr = attrs.get(field);
+ if (attr == null) {
+ attr = new AttributeInfo(this, field);
+ attrs.put(field, attr);
+ }
+ tag.setAttribute(attr);
+ }
+ }
+
+ // in the methods
+ for (MethodInfo m: selfMethods()) {
+ for (AttrTagInfo tag: m.comment().attrTags()) {
+ FieldInfo field = tag.reference();
+ if (field != null) {
+ AttributeInfo attr = attrs.get(field);
+ if (attr == null) {
+ attr = new AttributeInfo(this, field);
+ attrs.put(field, attr);
+ }
+ tag.setAttribute(attr);
+ attr.methods.add(m);
+ }
+ }
+ }
+
+ //constructors too
+ for (MethodInfo m: constructors()) {
+ for (AttrTagInfo tag: m.comment().attrTags()) {
+ FieldInfo field = tag.reference();
+ if (field != null) {
+ AttributeInfo attr = attrs.get(field);
+ if (attr == null) {
+ attr = new AttributeInfo(this, field);
+ attrs.put(field, attr);
+ }
+ tag.setAttribute(attr);
+ attr.methods.add(m);
+ }
+ }
+ }
+
+ mSelfAttributes = attrs.values().toArray(new AttributeInfo[attrs.size()]);
+ Arrays.sort(mSelfAttributes, AttributeInfo.comparator);
+ }
+ return mSelfAttributes;
+ }
+
+ public FieldInfo[] enumConstants()
+ {
+ return mEnumConstants;
+ }
+
+ public ClassInfo superclass()
+ {
+ if (!mSuperclassInit) {
+ if (this.checkLevel()) {
+ // rearrange our little inheritance hierarchy, because we need to hide classes that
+ // don't pass checkLevel
+ ClassInfo superclass = mRealSuperclass;
+ while (superclass != null && !superclass.checkLevel()) {
+ superclass = superclass.mRealSuperclass;
+ }
+ mSuperclass = superclass;
+ } else {
+ mSuperclass = mRealSuperclass;
+ }
+ }
+ return mSuperclass;
+ }
+
+ public ClassInfo realSuperclass()
+ {
+ return mRealSuperclass;
+ }
+
+ /** always the real superclass, not the collapsed one we get through superclass(),
+ * also has the type parameter info if it's generic.
+ */
+ public TypeInfo superclassType()
+ {
+ return mRealSuperclassType;
+ }
+
+ public TypeInfo asTypeInfo()
+ {
+ return mTypeInfo;
+ }
+
+ TypeInfo[] interfaceTypes()
+ {
+ ClassInfo[] infos = interfaces();
+ int len = infos.length;
+ TypeInfo[] types = new TypeInfo[len];
+ for (int i=0; i<len; i++) {
+ types[i] = infos[i].asTypeInfo();
+ }
+ return types;
+ }
+
+ public String htmlPage()
+ {
+ String s = containingPackage().name();
+ s = s.replace('.', '/');
+ s += '/';
+ s += name();
+ s += ".html";
+ s = DroidDoc.javadocDir + s;
+ return s;
+ }
+
+ /** Even indirectly */
+ public boolean isDerivedFrom(ClassInfo cl)
+ {
+ ClassInfo dad = this.superclass();
+ if (dad != null) {
+ if (dad.equals(cl)) {
+ return true;
+ } else {
+ if (dad.isDerivedFrom(cl)) {
+ return true;
+ }
+ }
+ }
+ for (ClassInfo iface: interfaces()) {
+ if (iface.equals(cl)) {
+ return true;
+ } else {
+ if (iface.isDerivedFrom(cl)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void makeKeywordEntries(List<KeywordEntry> keywords)
+ {
+ if (!checkLevel()) {
+ return;
+ }
+
+ String htmlPage = htmlPage();
+ String qualifiedName = qualifiedName();
+
+ keywords.add(new KeywordEntry(name(), htmlPage,
+ "class in " + containingPackage().name()));
+
+ FieldInfo[] fields = selfFields();
+ FieldInfo[] enumConstants = enumConstants();
+ MethodInfo[] ctors = constructors();
+ MethodInfo[] methods = selfMethods();
+
+ // enum constants
+ for (FieldInfo field: enumConstants()) {
+ if (field.checkLevel()) {
+ keywords.add(new KeywordEntry(field.name(),
+ htmlPage + "#" + field.anchor(),
+ "enum constant in " + qualifiedName));
+ }
+ }
+
+ // constants
+ for (FieldInfo field: fields) {
+ if (field.isConstant() && field.checkLevel()) {
+ keywords.add(new KeywordEntry(field.name(),
+ htmlPage + "#" + field.anchor(),
+ "constant in " + qualifiedName));
+ }
+ }
+
+ // fields
+ for (FieldInfo field: fields) {
+ if (!field.isConstant() && field.checkLevel()) {
+ keywords.add(new KeywordEntry(field.name(),
+ htmlPage + "#" + field.anchor(),
+ "field in " + qualifiedName));
+ }
+ }
+
+ // public constructors
+ for (MethodInfo m: ctors) {
+ if (m.isPublic() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "constructor in " + qualifiedName));
+ }
+ }
+
+ // protected constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+ for (MethodInfo m: ctors) {
+ if (m.isProtected() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "constructor in " + qualifiedName));
+ }
+ }
+ }
+
+ // package private constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+ for (MethodInfo m: ctors) {
+ if (m.isPackagePrivate() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "constructor in " + qualifiedName));
+ }
+ }
+ }
+
+ // private constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+ for (MethodInfo m: ctors) {
+ if (m.isPrivate() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "constructor in " + qualifiedName));
+ }
+ }
+ }
+
+ // public methods
+ for (MethodInfo m: methods) {
+ if (m.isPublic() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "method in " + qualifiedName));
+ }
+ }
+
+ // protected methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+ for (MethodInfo m: methods) {
+ if (m.isProtected() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "method in " + qualifiedName));
+ }
+ }
+ }
+
+ // package private methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+ for (MethodInfo m: methods) {
+ if (m.isPackagePrivate() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "method in " + qualifiedName));
+ }
+ }
+ }
+
+ // private methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+ for (MethodInfo m: methods) {
+ if (m.isPrivate() && m.checkLevel()) {
+ keywords.add(new KeywordEntry(m.name() + m.prettySignature(),
+ htmlPage + "#" + m.anchor(),
+ "method in " + qualifiedName));
+ }
+ }
+ }
+ }
+
+ public void makeLink(HDF data, String base)
+ {
+ data.setValue(base + ".label", this.name());
+ if (!this.isPrimitive() && this.isIncluded() && this.checkLevel()) {
+ data.setValue(base + ".link", this.htmlPage());
+ }
+ }
+
+ public static void makeLinkListHDF(HDF data, String base, ClassInfo[] classes) {
+ final int N = classes.length;
+ for (int i=0; i<N; i++) {
+ ClassInfo cl = classes[i];
+ if (cl.checkLevel()) {
+ cl.asTypeInfo().makeHDF(data, base + "." + i);
+ }
+ }
+ }
+
+ /**
+ * Used in lists of this class (packages, nested classes, known subclasses)
+ */
+ public void makeShortDescrHDF(HDF data, String base)
+ {
+ mTypeInfo.makeHDF(data, base + ".type");
+ data.setValue(base + ".kind", this.kind());
+ TagInfo.makeHDF(data, base + ".shortDescr", this.firstSentenceTags());
+ TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
+ }
+
+ /**
+ * Turns into the main class page
+ */
+ public void makeHDF(HDF data)
+ {
+ int i, j, n;
+ String name = name();
+ String qualified = qualifiedName();
+ AttributeInfo[] selfAttributes = selfAttributes();
+ MethodInfo[] methods = selfMethods();
+ FieldInfo[] fields = selfFields();
+ FieldInfo[] enumConstants = enumConstants();
+ MethodInfo[] ctors = constructors();
+ ClassInfo[] inners = innerClasses();
+
+ // class name
+ mTypeInfo.makeHDF(data, "class.type");
+ mTypeInfo.makeQualifiedHDF(data, "class.qualifiedType");
+ data.setValue("class.name", name);
+ data.setValue("class.qualified", qualified);
+ String scope = "";
+ if (isProtected()) {
+ data.setValue("class.scope", "protected");
+ }
+ else if (isPublic()) {
+ data.setValue("class.scope", "public");
+ }
+ if (isStatic()) {
+ data.setValue("class.static", "static");
+ }
+ if (isFinal()) {
+ data.setValue("class.final", "final");
+ }
+ if (isAbstract() && !isInterface()) {
+ data.setValue("class.abstract", "abstract");
+ }
+
+ // class info
+ String kind = kind();
+ if (kind != null) {
+ data.setValue("class.kind", kind);
+ }
+
+ // the containing package -- note that this can be passed to type_link,
+ // but it also contains the list of all of the packages
+ containingPackage().makeClassLinkListHDF(data, "class.package");
+
+ // inheritance hierarchy
+ Vector<ClassInfo> superClasses = new Vector<ClassInfo>();
+ superClasses.add(this);
+ ClassInfo supr = superclass();
+ while (supr != null) {
+ superClasses.add(supr);
+ supr = supr.superclass();
+ }
+ n = superClasses.size();
+ for (i=0; i<n; i++) {
+ supr = superClasses.elementAt(n-i-1);
+
+ supr.asTypeInfo().makeQualifiedHDF(data, "class.inheritance." + i + ".class");
+ supr.asTypeInfo().makeHDF(data, "class.inheritance." + i + ".short_class");
+ j = 0;
+ for (TypeInfo t: supr.interfaceTypes()) {
+ t.makeHDF(data, "class.inheritance." + i + ".interfaces." + j);
+ j++;
+ }
+ }
+
+ // class description
+ TagInfo.makeHDF(data, "class.descr", inlineTags());
+ TagInfo.makeHDF(data, "class.seeAlso", comment().seeTags());
+ TagInfo.makeHDF(data, "class.deprecated", deprecatedTags());
+
+ // known subclasses
+ TreeMap<String, ClassInfo> direct = new TreeMap<String, ClassInfo>();
+ TreeMap<String, ClassInfo> indirect = new TreeMap<String, ClassInfo>();
+ ClassInfo[] all = Converter.rootClasses();
+ for (ClassInfo cl: all) {
+ if (cl.superclass() != null && cl.superclass().equals(this)) {
+ direct.put(cl.name(), cl);
+ }
+ else if (cl.isDerivedFrom(this)) {
+ indirect.put(cl.name(), cl);
+ }
+ }
+ // direct
+ i = 0;
+ for (ClassInfo cl: direct.values()) {
+ if (cl.checkLevel()) {
+ cl.makeShortDescrHDF(data, "class.subclasses.direct." + i);
+ }
+ i++;
+ }
+ // indirect
+ i = 0;
+ for (ClassInfo cl: indirect.values()) {
+ if (cl.checkLevel()) {
+ cl.makeShortDescrHDF(data, "class.subclasses.indirect." + i);
+ }
+ i++;
+ }
+
+ // nested classes
+ i=0;
+ for (ClassInfo inner: inners) {
+ if (inner.checkLevel()) {
+ inner.makeShortDescrHDF(data, "class.inners." + i);
+ }
+ i++;
+ }
+
+ // enum constants
+ i=0;
+ for (FieldInfo field: enumConstants) {
+ if (field.isConstant()) {
+ field.makeHDF(data, "class.enumConstants." + i);
+ i++;
+ }
+ }
+
+ // constants
+ i=0;
+ for (FieldInfo field: fields) {
+ if (field.isConstant()) {
+ field.makeHDF(data, "class.constants." + i);
+ i++;
+ }
+ }
+
+ // fields
+ i=0;
+ for (FieldInfo field: fields) {
+ if (!field.isConstant()) {
+ field.makeHDF(data, "class.fields." + i);
+ i++;
+ }
+ }
+
+ // public constructors
+ i=0;
+ for (MethodInfo ctor: ctors) {
+ if (ctor.isPublic()) {
+ ctor.makeHDF(data, "class.ctors.public." + i);
+ i++;
+ }
+ }
+
+ // protected constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+ i=0;
+ for (MethodInfo ctor: ctors) {
+ if (ctor.isProtected()) {
+ ctor.makeHDF(data, "class.ctors.protected." + i);
+ i++;
+ }
+ }
+ }
+
+ // package private constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+ i=0;
+ for (MethodInfo ctor: ctors) {
+ if (ctor.isPackagePrivate()) {
+ ctor.makeHDF(data, "class.ctors.package." + i);
+ i++;
+ }
+ }
+ }
+
+ // private constructors
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+ i=0;
+ for (MethodInfo ctor: ctors) {
+ if (ctor.isPrivate()) {
+ ctor.makeHDF(data, "class.ctors.private." + i);
+ i++;
+ }
+ }
+ }
+
+ // public methods
+ i=0;
+ for (MethodInfo method: methods) {
+ if (method.isPublic()) {
+ method.makeHDF(data, "class.methods.public." + i);
+ i++;
+ }
+ }
+
+ // protected methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PROTECTED)) {
+ i=0;
+ for (MethodInfo method: methods) {
+ if (method.isProtected()) {
+ method.makeHDF(data, "class.methods.protected." + i);
+ i++;
+ }
+ }
+ }
+
+ // package private methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PACKAGE)) {
+ i=0;
+ for (MethodInfo method: methods) {
+ if (method.isPackagePrivate()) {
+ method.makeHDF(data, "class.methods.package." + i);
+ i++;
+ }
+ }
+ }
+
+ // private methods
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_PRIVATE)) {
+ i=0;
+ for (MethodInfo method: methods) {
+ if (method.isPrivate()) {
+ method.makeHDF(data, "class.methods.private." + i);
+ i++;
+ }
+ }
+ }
+
+ // xml attributes
+ i=0;
+ for (AttributeInfo attr: selfAttributes) {
+ if (attr.checkLevel()) {
+ attr.makeHDF(data, "class.attrs." + i);
+ i++;
+ }
+ }
+
+ // inherited methods
+ Set<ClassInfo> interfaces = new TreeSet<ClassInfo>();
+ addInterfaces(interfaces(), interfaces);
+ ClassInfo cl = superclass();
+ i=0;
+ while (cl != null) {
+ addInterfaces(cl.interfaces(), interfaces);
+ makeInheritedHDF(data, i, cl);
+ cl = cl.superclass();
+ i++;
+ }
+ for (ClassInfo iface: interfaces) {
+ makeInheritedHDF(data, i, iface);
+ i++;
+ }
+ }
+
+ private static void addInterfaces(ClassInfo[] ifaces, Set<ClassInfo> out)
+ {
+ for (ClassInfo cl: ifaces) {
+ out.add(cl);
+ addInterfaces(cl.interfaces(), out);
+ }
+ }
+
+ private static void makeInheritedHDF(HDF data, int index, ClassInfo cl)
+ {
+ int i;
+
+ String base = "class.inherited." + index;
+ data.setValue(base + ".qualified", cl.qualifiedName());
+ if (cl.checkLevel()) {
+ data.setValue(base + ".link", cl.htmlPage());
+ }
+ String kind = cl.kind();
+ if (kind != null) {
+ data.setValue(base + ".kind", kind);
+ }
+
+ // xml attributes
+ i=0;
+ for (AttributeInfo attr: cl.selfAttributes()) {
+ attr.makeHDF(data, base + ".attrs." + i);
+ i++;
+ }
+
+ // methods
+ i=0;
+ for (MethodInfo method: cl.selfMethods()) {
+ method.makeHDF(data, base + ".methods." + i);
+ i++;
+ }
+
+ // fields
+ i=0;
+ for (FieldInfo field: cl.selfFields()) {
+ if (!field.isConstant()) {
+ field.makeHDF(data, base + ".fields." + i);
+ i++;
+ }
+ }
+
+ // constants
+ i=0;
+ for (FieldInfo field: cl.selfFields()) {
+ if (field.isConstant()) {
+ field.makeHDF(data, base + ".constants." + i);
+ i++;
+ }
+ }
+ }
+
+ public boolean isHidden()
+ {
+ int val = mHidden;
+ if (val >= 0) {
+ return val != 0;
+ } else {
+ boolean v = isHiddenImpl();
+ mHidden = v ? 1 : 0;
+ return v;
+ }
+ }
+
+ public boolean isHiddenImpl()
+ {
+ ClassInfo cl = this;
+ while (cl != null) {
+ PackageInfo pkg = cl.containingPackage();
+ if (pkg.isHidden()) {
+ return true;
+ }
+ if (cl.comment().isHidden()) {
+ return true;
+ }
+ cl = cl.containingClass();
+ }
+ return false;
+ }
+
+ private MethodInfo matchMethod(MethodInfo[] methods, String name,
+ String[] params, String[] dimensions)
+ {
+ int len = methods.length;
+ for (int i=0; i<len; i++) {
+ MethodInfo method = methods[i];
+ if (method.name().equals(name)) {
+ if (params == null) {
+ return method;
+ } else {
+ if (method.matchesParams(params, dimensions)) {
+ return method;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public MethodInfo findMethod(String name,
+ String[] params, String[] dimensions)
+ {
+ // first look on our class, and our superclasses
+
+ // for methods
+ MethodInfo rv;
+ rv = matchMethod(methods(), name, params, dimensions);
+
+ if (rv != null) {
+ return rv;
+ }
+
+ // for constructors
+ rv = matchMethod(constructors(), name, params, dimensions);
+ if (rv != null) {
+ return rv;
+ }
+
+ // then recursively look at our containing class
+ ClassInfo containing = containingClass();
+ if (containing != null) {
+ return containing.findMethod(name, params, dimensions);
+ }
+
+ return null;
+ }
+
+ private ClassInfo searchInnerClasses(String[] nameParts, int index)
+ {
+ String part = nameParts[index];
+
+ ClassInfo[] inners = mInnerClasses;
+ for (ClassInfo in: inners) {
+ String[] innerParts = in.nameParts();
+ if (part.equals(innerParts[innerParts.length-1])) {
+ if (index == nameParts.length-1) {
+ return in;
+ } else {
+ return in.searchInnerClasses(nameParts, index+1);
+ }
+ }
+ }
+ return null;
+ }
+
+ public ClassInfo extendedFindClass(String className)
+ {
+ // ClassDoc.findClass has this bug that we're working around here:
+ // If you have a class PackageManager with an inner class PackageInfo
+ // and you call it with "PackageInfo" it doesn't find it.
+ return searchInnerClasses(className.split("\\."), 0);
+ }
+
+ public ClassInfo findClass(String className)
+ {
+ return Converter.obtainClass(mClass.findClass(className));
+ }
+
+ public ClassInfo findInnerClass(String className)
+ {
+ // ClassDoc.findClass won't find inner classes. To deal with that,
+ // we try what they gave us first, but if that didn't work, then
+ // we see if there are any periods in className, and start searching
+ // from there.
+ String[] nodes = className.split("\\.");
+ ClassDoc cl = mClass;
+ for (String n: nodes) {
+ cl = cl.findClass(n);
+ if (cl == null) {
+ return null;
+ }
+ }
+ return Converter.obtainClass(cl);
+ }
+
+ public FieldInfo findField(String name)
+ {
+ // first look on our class, and our superclasses
+ for (FieldInfo f: fields()) {
+ if (f.name().equals(name)) {
+ return f;
+ }
+ }
+
+ // then look at our enum constants (these are really fields, maybe
+ // they should be mixed into fields(). not sure)
+ for (FieldInfo f: enumConstants()) {
+ if (f.name().equals(name)) {
+ return f;
+ }
+ }
+
+ // then recursively look at our containing class
+ ClassInfo containing = containingClass();
+ if (containing != null) {
+ return containing.findField(name);
+ }
+
+ return null;
+ }
+
+ public static ClassInfo[] sortByName(ClassInfo[] classes)
+ {
+ int i;
+ Sorter[] sorted = new Sorter[classes.length];
+ for (i=0; i<sorted.length; i++) {
+ ClassInfo cl = classes[i];
+ sorted[i] = new Sorter(cl.name(), cl);
+ }
+
+ Arrays.sort(sorted);
+
+ ClassInfo[] rv = new ClassInfo[classes.length];
+ for (i=0; i<rv.length; i++) {
+ rv[i] = (ClassInfo)sorted[i].data;
+ }
+
+ return rv;
+ }
+
+ public boolean equals(ClassInfo that)
+ {
+ if (that != null) {
+ return this.qualifiedName().equals(that.qualifiedName());
+ } else {
+ return false;
+ }
+ }
+
+ public void setNonWrittenConstructors(MethodInfo[] nonWritten) {
+ mNonWrittenConstructors = nonWritten;
+ }
+
+ public MethodInfo[] getNonWrittenConstructors() {
+ return mNonWrittenConstructors;
+ }
+
+ public String kind()
+ {
+ if (isOrdinaryClass()) {
+ return "class";
+ }
+ else if (isInterface()) {
+ return "interface";
+ }
+ else if (isEnum()) {
+ return "enum";
+ }
+ else if (isError()) {
+ return "class";
+ }
+ else if (isException()) {
+ return "class";
+ }
+ else if (isAnnotation()) {
+ return "@interface";
+ }
+ return null;
+ }
+
+ public void setHiddenMethods(MethodInfo[] mInfo){
+ mHiddenMethods = mInfo;
+ }
+ public MethodInfo[] getHiddenMethods(){
+ return mHiddenMethods;
+ }
+ public String toString(){
+ return this.qualifiedName();
+ }
+
+ public void setReasonIncluded(String reason) {
+ mReasonIncluded = reason;
+ }
+
+ public String getReasonIncluded() {
+ return mReasonIncluded;
+ }
+
+ private ClassDoc mClass;
+
+ // ctor
+ private boolean mIsPublic;
+ private boolean mIsProtected;
+ private boolean mIsPackagePrivate;
+ private boolean mIsPrivate;
+ private boolean mIsStatic;
+ private boolean mIsInterface;
+ private boolean mIsAbstract;
+ private boolean mIsOrdinaryClass;
+ private boolean mIsException;
+ private boolean mIsError;
+ private boolean mIsEnum;
+ private boolean mIsAnnotation;
+ private boolean mIsFinal;
+ private boolean mIsIncluded;
+ private String mName;
+ private String mQualifiedName;
+ private String mQualifiedTypeName;
+ private boolean mIsPrimitive;
+ private TypeInfo mTypeInfo;
+ private String[] mNameParts;
+
+ // init
+ private ClassInfo[] mRealInterfaces;
+ private ClassInfo[] mInterfaces;
+ private TypeInfo[] mRealInterfaceTypes;
+ private ClassInfo[] mInnerClasses;
+ private MethodInfo[] mAllConstructors;
+ private MethodInfo[] mAllSelfMethods;
+ private MethodInfo[] mAnnotationElements; // if this class is an annotation
+ private FieldInfo[] mAllSelfFields;
+ private FieldInfo[] mEnumConstants;
+ private PackageInfo mContainingPackage;
+ private ClassInfo mContainingClass;
+ private ClassInfo mRealSuperclass;
+ private TypeInfo mRealSuperclassType;
+ private ClassInfo mSuperclass;
+ private AnnotationInstanceInfo[] mAnnotations;
+ private boolean mSuperclassInit;
+ private boolean mDeprecatedKnown;
+
+ // lazy
+ private MethodInfo[] mConstructors;
+ private ClassInfo[] mRealInnerClasses;
+ private MethodInfo[] mSelfMethods;
+ private FieldInfo[] mSelfFields;
+ private AttributeInfo[] mSelfAttributes;
+ private MethodInfo[] mMethods;
+ private FieldInfo[] mFields;
+ private TypeInfo[] mTypeParameters;
+ private MethodInfo[] mHiddenMethods;
+ private int mHidden = -1;
+ private int mCheckLevel = -1;
+ private String mReasonIncluded;
+ private MethodInfo[] mNonWrittenConstructors;
+ private boolean mIsDeprecated;
+}
diff --git a/tools/droiddoc/src/ClearPage.java b/tools/droiddoc/src/ClearPage.java
new file mode 100644
index 000000000..2a8fcede8
--- /dev/null
+++ b/tools/droiddoc/src/ClearPage.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.sun.javadoc.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+
+public class ClearPage
+{
+ /*
+ public ClearPage()
+ {
+ String templ = "templates/index.cs";
+ String filename = "docs/index.html";
+
+ data.setValue("A.B.C", "1");
+ data.setValue("A.B.D", "2");
+ }
+ */
+
+ public static ArrayList<String> hdfFiles = new ArrayList<String>();
+
+ private static ArrayList<String> mTemplateDirs = new ArrayList<String>();
+ private static boolean mTemplateDirSet = false;
+
+ public static String outputDir = "docs";
+ public static String htmlDir = null;
+ public static String toroot = null;
+
+ public static void addTemplateDir(String dir)
+ {
+ mTemplateDirSet = true;
+ mTemplateDirs.add(dir);
+
+ File hdfFile = new File(dir, "data.hdf");
+ if (hdfFile.canRead()) {
+ hdfFiles.add(hdfFile.getPath());
+ }
+ }
+
+ private static int countSlashes(String s)
+ {
+ final int N = s.length();
+ int slashcount = 0;
+ for (int i=0; i<N; i++) {
+ if (s.charAt(i) == '/') {
+ slashcount++;
+ }
+ }
+ return slashcount;
+ }
+
+ public static void write(HDF data, String templ, String filename)
+ {
+ write(data, templ, filename, false);
+ }
+
+ public static void write(HDF data, String templ, String filename, boolean fullPath)
+ {
+ if (htmlDir != null) {
+ data.setValue("hasindex", "true");
+ }
+
+ String toroot;
+ if (ClearPage.toroot != null) {
+ toroot = ClearPage.toroot;
+ } else {
+ int slashcount = countSlashes(filename);
+ if (slashcount > 0) {
+ toroot = "";
+ for (int i=0; i<slashcount; i++) {
+ toroot += "../";
+ }
+ } else {
+ toroot = "./";
+ }
+ }
+ data.setValue("toroot", toroot);
+
+ data.setValue("filename", filename);
+
+ if (!fullPath) {
+ filename = outputDir + "/" + filename;
+ }
+
+ int i=0;
+ if (htmlDir != null) {
+ data.setValue("hdf.loadpaths." + i, htmlDir);
+ i++;
+ }
+ if (mTemplateDirSet) {
+ for (String dir: mTemplateDirs) {
+ data.setValue("hdf.loadpaths." + i, dir);
+ i++;
+ }
+ } else {
+ data.setValue("hdf.loadpaths." + i, "templates");
+ }
+
+ CS cs = new CS(data);
+ cs.parseFile(templ);
+
+ File file = new File(outputFilename(filename));
+
+ ensureDirectory(file);
+
+ OutputStreamWriter stream = null;
+ try {
+ stream = new OutputStreamWriter(
+ new FileOutputStream(file));
+ String rendered = cs.render();
+ stream.write(rendered, 0, rendered.length());
+ }
+ catch (IOException e) {
+ System.out.println("error: " + e.getMessage() + "; when writing file: " + filename);
+ }
+ finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ }
+ catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ // recursively create the directories to the output
+ public static void ensureDirectory(File f)
+ {
+ File parent = f.getParentFile();
+ if (parent != null) {
+ parent.mkdirs();
+ }
+ }
+
+ public static void copyFile(File from, String toPath)
+ {
+ File to = new File(outputDir + "/" + toPath);
+ FileInputStream in;
+ FileOutputStream out;
+ try {
+ if (!from.exists()) {
+ throw new IOException();
+ }
+ in = new FileInputStream(from);
+ }
+ catch (IOException e) {
+ System.err.println(from.getAbsolutePath() + ": Error opening file");
+ return ;
+ }
+ ensureDirectory(to);
+ try {
+ out = new FileOutputStream(to);
+ }
+ catch (IOException e) {
+ System.err.println(from.getAbsolutePath() + ": Error opening file");
+ return ;
+ }
+
+ long sizel = from.length();
+ final int maxsize = 64*1024;
+ int size = sizel > maxsize ? maxsize : (int)sizel;
+ byte[] buf = new byte[size];
+ while (true) {
+ try {
+ size = in.read(buf);
+ }
+ catch (IOException e) {
+ System.err.println(from.getAbsolutePath()
+ + ": error reading file");
+ break;
+ }
+ if (size > 0) {
+ try {
+ out.write(buf, 0, size);
+ }
+ catch (IOException e) {
+ System.err.println(from.getAbsolutePath()
+ + ": error writing file");
+ }
+ } else {
+ break;
+ }
+ }
+ try {
+ in.close();
+ }
+ catch (IOException e) {
+ }
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ }
+ }
+
+ /** Takes a string that ends w/ .html and changes the .html to htmlExtension */
+ public static String outputFilename(String htmlFile) {
+ if (!DroidDoc.htmlExtension.equals(".html") && htmlFile.endsWith(".html")) {
+ return htmlFile.substring(0, htmlFile.length()-5) + DroidDoc.htmlExtension;
+ } else {
+ return htmlFile;
+ }
+ }
+
+}
diff --git a/tools/droiddoc/src/Comment.java b/tools/droiddoc/src/Comment.java
new file mode 100644
index 000000000..3a24357f6
--- /dev/null
+++ b/tools/droiddoc/src/Comment.java
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+
+public class Comment
+{
+ static final Pattern LEADING_WHITESPACE = Pattern.compile(
+ "^[ \t\n\r]*(.*)$",
+ Pattern.DOTALL);
+
+ static final Pattern TAG_BEGIN = Pattern.compile(
+ "[\r\n][\r\n \t]*@",
+ Pattern.DOTALL);
+
+ static final Pattern TAG = Pattern.compile(
+ "(@[^ \t\r\n]+)[ \t\r\n]+(.*)",
+ Pattern.DOTALL);
+
+ static final Pattern INLINE_TAG = Pattern.compile(
+ "(.*?)\\{(@[^ \t\r\n\\}]+)[ \t\r\n]*(.*?)\\}",
+ Pattern.DOTALL);
+
+ static final Pattern FIRST_SENTENCE = Pattern.compile(
+ "((.*?)\\.)[ \t\r\n\\<](.*)",
+ Pattern.DOTALL);
+
+ private static final String[] KNOWN_TAGS = new String[] {
+ "@author",
+ "@since",
+ "@version",
+ "@deprecated",
+ "@undeprecate",
+ "@docRoot",
+ "@inheritDoc",
+ "@more",
+ "@code",
+ "@samplecode",
+ "@sample",
+ "@include",
+ "@serial",
+ "@com.intel.drl.spec_ref",
+ "@ar.org.fitc.spec_ref",
+ };
+
+ public Comment(String text, ContainerInfo base, SourcePositionInfo sp)
+ {
+ mText = text;
+ mBase = base;
+ // sp now points to the end of the text, not the beginning!
+ mPosition = SourcePositionInfo.findBeginning(sp, text);
+ }
+
+ private void parseRegex(String text)
+ {
+ Matcher m;
+
+ m = LEADING_WHITESPACE.matcher(text);
+ m.matches();
+ text = m.group(1);
+
+ m = TAG_BEGIN.matcher(text);
+
+ int start = 0;
+ int end = 0;
+ while (m.find()) {
+ end = m.start();
+
+ tag(text, start, end);
+
+ start = m.end()-1; // -1 is the @
+ }
+ end = text.length();
+ tag(text, start, end);
+ }
+
+ private void tag(String text, int start, int end)
+ {
+ SourcePositionInfo pos = SourcePositionInfo.add(mPosition, mText, start);
+
+ if (start >= 0 && end > 0 && (end-start) > 0) {
+ text = text.substring(start, end);
+
+ Matcher m = TAG.matcher(text);
+ if (m.matches()) {
+ // out of line tag
+ tag(m.group(1), m.group(2), false, pos);
+ } else {
+ // look for inline tags
+ m = INLINE_TAG.matcher(text);
+ start = 0;
+ while (m.find()) {
+ String str = m.group(1);
+ String tagname = m.group(2);
+ String tagvalue = m.group(3);
+ tag(null, m.group(1), true, pos);
+ tag(tagname, tagvalue, true, pos);
+ start = m.end();
+ }
+ int len = text.length();
+ if (start != len) {
+ tag(null, text.substring(start), true, pos);
+ }
+ }
+ }
+ }
+
+ private void tag(String name, String text, boolean isInline, SourcePositionInfo pos)
+ {
+ /*
+ String s = isInline ? "inline" : "outofline";
+ System.out.println("---> " + s
+ + " name=[" + name + "] text=[" + text + "]");
+ */
+ if (name == null) {
+ mInlineTagsList.add(new TextTagInfo("Text", "Text", text, pos));
+ }
+ else if (name.equals("@param")) {
+ mParamTagsList.add(new ParamTagInfo("@param", "@param", text, mBase, pos));
+ }
+ else if (name.equals("@see")) {
+ mSeeTagsList.add(new SeeTagInfo("@see", "@see", text, mBase, pos));
+ }
+ else if (name.equals("@link") || name.equals("@linkplain")) {
+ mInlineTagsList.add(new SeeTagInfo(name, "@see", text, mBase, pos));
+ }
+ else if (name.equals("@throws") || name.equals("@exception")) {
+ mThrowsTagsList.add(new ThrowsTagInfo("@throws", "@throws", text, mBase, pos));
+ }
+ else if (name.equals("@return")) {
+ mReturnTagsList.add(new ParsedTagInfo("@return", "@return", text, mBase, pos));
+ }
+ else if (name.equals("@deprecated")) {
+ if (text.length() == 0) {
+ Errors.error(Errors.MISSING_COMMENT, pos,
+ "@deprecated tag with no explanatory comment");
+ text = "No replacement.";
+ }
+ mDeprecatedTagsList.add(new ParsedTagInfo("@deprecated", "@deprecated", text, mBase, pos));
+ }
+ else if (name.equals("@literal")) {
+ mInlineTagsList.add(new LiteralTagInfo(name, name, text, pos));
+ }
+ else if (name.equals("@hide") || name.equals("@doconly")) {
+ // nothing
+ }
+ else if (name.equals("@attr")) {
+ AttrTagInfo tag = new AttrTagInfo("@attr", "@attr", text, mBase, pos);
+ mAttrTagsList.add(tag);
+ Comment c = tag.description();
+ if (c != null) {
+ for (TagInfo t: c.tags()) {
+ mInlineTagsList.add(t);
+ }
+ }
+ }
+ else if (name.equals("@undeprecate")) {
+ mUndeprecateTagsList.add(new TextTagInfo("@undeprecate", "@undeprecate", text, pos));
+ }
+ else if (name.equals("@include") || name.equals("@sample")) {
+ mInlineTagsList.add(new SampleTagInfo(name, "@include", text, mBase, pos));
+ }
+ else {
+ boolean known = false;
+ for (String s: KNOWN_TAGS) {
+ if (s.equals(name)) {
+ known = true;
+ break;
+ }
+ }
+ if (!known) {
+ Errors.error(Errors.UNKNOWN_TAG, pos == null ? null : new SourcePositionInfo(pos),
+ "Unknown tag: " + name);
+ }
+ TagInfo t = new TextTagInfo(name, name, text, pos);
+ if (isInline) {
+ mInlineTagsList.add(t);
+ } else {
+ mTagsList.add(t);
+ }
+ }
+ }
+
+ private void parseBriefTags()
+ {
+ int N = mInlineTagsList.size();
+
+ // look for "@more" tag, which means that we might go past the first sentence.
+ int more = -1;
+ for (int i=0; i<N; i++) {
+ if (mInlineTagsList.get(i).name().equals("@more")) {
+ more = i;
+ }
+ }
+ if (more >= 0) {
+ for (int i=0; i<more; i++) {
+ mBriefTagsList.add(mInlineTagsList.get(i));
+ }
+ } else {
+ for (int i=0; i<N; i++) {
+ TagInfo t = mInlineTagsList.get(i);
+ if (t.name().equals("Text")) {
+ Matcher m = FIRST_SENTENCE.matcher(t.text());
+ if (m.matches()) {
+ String text = m.group(1);
+ TagInfo firstSentenceTag = new TagInfo(t.name(), t.kind(), text, t.position());
+ mBriefTagsList.add(firstSentenceTag);
+ break;
+ }
+ }
+ mBriefTagsList.add(t);
+
+ }
+ }
+ }
+
+ public TagInfo[] tags()
+ {
+ init();
+ return mInlineTags;
+ }
+
+ public TagInfo[] tags(String name)
+ {
+ init();
+ ArrayList<TagInfo> results = new ArrayList<TagInfo>();
+ int N = mInlineTagsList.size();
+ for (int i=0; i<N; i++) {
+ TagInfo t = mInlineTagsList.get(i);
+ if (t.name().equals(name)) {
+ results.add(t);
+ }
+ }
+ return results.toArray(new TagInfo[results.size()]);
+ }
+
+ public ParamTagInfo[] paramTags()
+ {
+ init();
+ return mParamTags;
+ }
+
+ public SeeTagInfo[] seeTags()
+ {
+ init();
+ return mSeeTags;
+ }
+
+ public ThrowsTagInfo[] throwsTags()
+ {
+ init();
+ return mThrowsTags;
+ }
+
+ public TagInfo[] returnTags()
+ {
+ init();
+ return mReturnTags;
+ }
+
+ public TagInfo[] deprecatedTags()
+ {
+ init();
+ return mDeprecatedTags;
+ }
+
+ public TagInfo[] undeprecateTags()
+ {
+ init();
+ return mUndeprecateTags;
+ }
+
+ public AttrTagInfo[] attrTags()
+ {
+ init();
+ return mAttrTags;
+ }
+
+ public TagInfo[] briefTags()
+ {
+ init();
+ return mBriefTags;
+ }
+
+ public boolean isHidden()
+ {
+ if (mHidden >= 0) {
+ return mHidden != 0;
+ } else {
+ if (DroidDoc.checkLevel(DroidDoc.SHOW_HIDDEN)) {
+ mHidden = 0;
+ return false;
+ }
+ boolean b = mText.indexOf("@hide") >= 0;
+ mHidden = b ? 1 : 0;
+ return b;
+ }
+ }
+
+ public boolean isDocOnly() {
+ if (mDocOnly >= 0) {
+ return mDocOnly != 0;
+ } else {
+ boolean b = (mText != null) && (mText.indexOf("@doconly") >= 0);
+ mDocOnly = b ? 1 : 0;
+ return b;
+ }
+ }
+
+ private void init()
+ {
+ if (!mInitialized) {
+ initImpl();
+ }
+ }
+
+ private void initImpl()
+ {
+ isHidden();
+ isDocOnly();
+ parseRegex(mText);
+ parseBriefTags();
+ mText = null;
+ mInitialized = true;
+
+ mInlineTags = mInlineTagsList.toArray(new TagInfo[mInlineTagsList.size()]);
+ mParamTags = mParamTagsList.toArray(new ParamTagInfo[mParamTagsList.size()]);
+ mSeeTags = mSeeTagsList.toArray(new SeeTagInfo[mSeeTagsList.size()]);
+ mThrowsTags = mThrowsTagsList.toArray(new ThrowsTagInfo[mThrowsTagsList.size()]);
+ mReturnTags = ParsedTagInfo.joinTags(mReturnTagsList.toArray(
+ new ParsedTagInfo[mReturnTagsList.size()]));
+ mDeprecatedTags = ParsedTagInfo.joinTags(mDeprecatedTagsList.toArray(
+ new ParsedTagInfo[mDeprecatedTagsList.size()]));
+ mUndeprecateTags = mUndeprecateTagsList.toArray(new TagInfo[mUndeprecateTagsList.size()]);
+ mAttrTags = mAttrTagsList.toArray(new AttrTagInfo[mAttrTagsList.size()]);
+ mBriefTags = mBriefTagsList.toArray(new TagInfo[mBriefTagsList.size()]);
+
+ mParamTagsList = null;
+ mSeeTagsList = null;
+ mThrowsTagsList = null;
+ mReturnTagsList = null;
+ mDeprecatedTagsList = null;
+ mUndeprecateTagsList = null;
+ mAttrTagsList = null;
+ mBriefTagsList = null;
+ }
+
+ boolean mInitialized;
+ int mHidden = -1;
+ int mDocOnly = -1;
+ String mText;
+ ContainerInfo mBase;
+ SourcePositionInfo mPosition;
+ int mLine = 1;
+
+ TagInfo[] mInlineTags;
+ TagInfo[] mTags;
+ ParamTagInfo[] mParamTags;
+ SeeTagInfo[] mSeeTags;
+ ThrowsTagInfo[] mThrowsTags;
+ TagInfo[] mBriefTags;
+ TagInfo[] mReturnTags;
+ TagInfo[] mDeprecatedTags;
+ TagInfo[] mUndeprecateTags;
+ AttrTagInfo[] mAttrTags;
+
+ ArrayList<TagInfo> mInlineTagsList = new ArrayList<TagInfo>();
+ ArrayList<TagInfo> mTagsList = new ArrayList<TagInfo>();
+ ArrayList<ParamTagInfo> mParamTagsList = new ArrayList<ParamTagInfo>();
+ ArrayList<SeeTagInfo> mSeeTagsList = new ArrayList<SeeTagInfo>();
+ ArrayList<ThrowsTagInfo> mThrowsTagsList = new ArrayList<ThrowsTagInfo>();
+ ArrayList<TagInfo> mBriefTagsList = new ArrayList<TagInfo>();
+ ArrayList<ParsedTagInfo> mReturnTagsList = new ArrayList<ParsedTagInfo>();
+ ArrayList<ParsedTagInfo> mDeprecatedTagsList = new ArrayList<ParsedTagInfo>();
+ ArrayList<TagInfo> mUndeprecateTagsList = new ArrayList<TagInfo>();
+ ArrayList<AttrTagInfo> mAttrTagsList = new ArrayList<AttrTagInfo>();
+
+
+}
diff --git a/tools/droiddoc/src/ContainerInfo.java b/tools/droiddoc/src/ContainerInfo.java
new file mode 100644
index 000000000..b8a3e100c
--- /dev/null
+++ b/tools/droiddoc/src/ContainerInfo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface ContainerInfo
+{
+ public String qualifiedName();
+ public boolean checkLevel();
+}
diff --git a/tools/droiddoc/src/Converter.java b/tools/droiddoc/src/Converter.java
new file mode 100644
index 000000000..4014f7f08
--- /dev/null
+++ b/tools/droiddoc/src/Converter.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+
+public class Converter
+{
+ private static RootDoc root;
+
+ public static void makeInfo(RootDoc r)
+ {
+ root = r;
+
+ int N, i;
+
+ // create the objects
+ ClassDoc[] classDocs = r.classes();
+ N = classDocs.length;
+ for (i=0; i<N; i++) {
+ Converter.obtainClass(classDocs[i]);
+ }
+ ArrayList<ClassInfo> classesNeedingInit2 = new ArrayList<ClassInfo>();
+ // fill in the fields that reference other classes
+ while (mClassesNeedingInit.size() > 0) {
+ i = mClassesNeedingInit.size()-1;
+ ClassNeedingInit clni = mClassesNeedingInit.get(i);
+ mClassesNeedingInit.remove(i);
+
+ initClass(clni.c, clni.cl);
+ classesNeedingInit2.add(clni.cl);
+ }
+ mClassesNeedingInit = null;
+ for (ClassInfo cl: classesNeedingInit2) {
+ cl.init2();
+ }
+
+ finishAnnotationValueInit();
+
+ // fill in the "root" stuff
+ mRootClasses = Converter.convertClasses(r.classes());
+ }
+
+ private static ClassInfo[] mRootClasses;
+ public static ClassInfo[] rootClasses()
+ {
+ return mRootClasses;
+ }
+
+ public static ClassInfo[] allClasses() {
+ return (ClassInfo[])mClasses.all();
+ }
+
+ private static void initClass(ClassDoc c, ClassInfo cl)
+ {
+ MethodDoc[] annotationElements;
+ if (c instanceof AnnotationTypeDoc) {
+ annotationElements = ((AnnotationTypeDoc)c).elements();
+ } else {
+ annotationElements = new MethodDoc[0];
+ }
+ cl.init(Converter.obtainType(c),
+ Converter.convertClasses(c.interfaces()),
+ Converter.convertTypes(c.interfaceTypes()),
+ Converter.convertClasses(c.innerClasses()),
+ Converter.convertMethods(c.constructors(false)),
+ Converter.convertMethods(c.methods(false)),
+ Converter.convertMethods(annotationElements),
+ Converter.convertFields(c.fields(false)),
+ Converter.convertFields(c.enumConstants()),
+ Converter.obtainPackage(c.containingPackage()),
+ Converter.obtainClass(c.containingClass()),
+ Converter.obtainClass(c.superclass()),
+ Converter.obtainType(c.superclassType()),
+ Converter.convertAnnotationInstances(c.annotations())
+ );
+ cl.setHiddenMethods(Converter.getHiddenMethods(c.methods(false)));
+ cl.setNonWrittenConstructors(Converter.convertNonWrittenConstructors(c.constructors(false)));
+ cl.init3(Converter.convertTypes(c.typeParameters()), Converter.convertClasses(c.innerClasses(false)));
+ }
+
+ public static ClassInfo obtainClass(String className)
+ {
+ return Converter.obtainClass(root.classNamed(className));
+ }
+
+ public static PackageInfo obtainPackage(String packageName)
+ {
+ return Converter.obtainPackage(root.packageNamed(packageName));
+ }
+
+ private static TagInfo convertTag(Tag tag)
+ {
+ return new TextTagInfo(tag.name(), tag.kind(), tag.text(),
+ Converter.convertSourcePosition(tag.position()));
+ }
+
+ private static ThrowsTagInfo convertThrowsTag(ThrowsTag tag,
+ ContainerInfo base)
+ {
+ return new ThrowsTagInfo(tag.name(), tag.text(), tag.kind(),
+ Converter.obtainClass(tag.exception()),
+ tag.exceptionComment(), base,
+ Converter.convertSourcePosition(tag.position()));
+ }
+
+ private static ParamTagInfo convertParamTag(ParamTag tag,
+ ContainerInfo base)
+ {
+ return new ParamTagInfo(tag.name(), tag.kind(), tag.text(),
+ tag.isTypeParameter(), tag.parameterComment(),
+ tag.parameterName(),
+ base,
+ Converter.convertSourcePosition(tag.position()));
+ }
+
+ private static SeeTagInfo convertSeeTag(SeeTag tag, ContainerInfo base)
+ {
+ return new SeeTagInfo(tag.name(), tag.kind(), tag.text(), base,
+ Converter.convertSourcePosition(tag.position()));
+ }
+
+ private static SourcePositionInfo convertSourcePosition(SourcePosition sp)
+ {
+ if (sp == null) {
+ return null;
+ }
+ return new SourcePositionInfo(sp.file().toString(), sp.line(),
+ sp.column());
+ }
+
+ public static TagInfo[] convertTags(Tag[] tags, ContainerInfo base)
+ {
+ int len = tags.length;
+ TagInfo[] out = new TagInfo[len];
+ for (int i=0; i<len; i++) {
+ Tag t = tags[i];
+ /*
+ System.out.println("Tag name='" + t.name() + "' kind='"
+ + t.kind() + "'");
+ */
+ if (t instanceof SeeTag) {
+ out[i] = Converter.convertSeeTag((SeeTag)t, base);
+ }
+ else if (t instanceof ThrowsTag) {
+ out[i] = Converter.convertThrowsTag((ThrowsTag)t, base);
+ }
+ else if (t instanceof ParamTag) {
+ out[i] = Converter.convertParamTag((ParamTag)t, base);
+ }
+ else {
+ out[i] = Converter.convertTag(t);
+ }
+ }
+ return out;
+ }
+
+ public static ClassInfo[] convertClasses(ClassDoc[] classes)
+ {
+ if (classes == null) return null;
+ int N = classes.length;
+ ClassInfo[] result = new ClassInfo[N];
+ for (int i=0; i<N; i++) {
+ result[i] = Converter.obtainClass(classes[i]);
+ }
+ return result;
+ }
+
+ private static ParameterInfo convertParameter(Parameter p, SourcePosition pos)
+ {
+ if (p == null) return null;
+ ParameterInfo pi = new ParameterInfo(p.name(), p.typeName(),
+ Converter.obtainType(p.type()),
+ Converter.convertSourcePosition(pos));
+ return pi;
+ }
+
+ private static ParameterInfo[] convertParameters(Parameter[] p, MemberDoc m)
+ {
+ SourcePosition pos = m.position();
+ int len = p.length;
+ ParameterInfo[] q = new ParameterInfo[len];
+ for (int i=0; i<len; i++) {
+ q[i] = Converter.convertParameter(p[i], pos);
+ }
+ return q;
+ }
+
+ private static TypeInfo[] convertTypes(Type[] p)
+ {
+ if (p == null) return null;
+ int len = p.length;
+ TypeInfo[] q = new TypeInfo[len];
+ for (int i=0; i<len; i++) {
+ q[i] = Converter.obtainType(p[i]);
+ }
+ return q;
+ }
+
+ private Converter()
+ {
+ }
+
+ private static class ClassNeedingInit
+ {
+ ClassNeedingInit(ClassDoc c, ClassInfo cl)
+ {
+ this.c = c;
+ this.cl = cl;
+ }
+ ClassDoc c;
+ ClassInfo cl;
+ };
+ private static ArrayList<ClassNeedingInit> mClassesNeedingInit
+ = new ArrayList<ClassNeedingInit>();
+
+ static ClassInfo obtainClass(ClassDoc o)
+ {
+ return (ClassInfo)mClasses.obtain(o);
+ }
+ private static Cache mClasses = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ ClassDoc c = (ClassDoc)o;
+ ClassInfo cl = new ClassInfo(
+ c,
+ c.getRawCommentText(),
+ Converter.convertSourcePosition(c.position()),
+ c.isPublic(),
+ c.isProtected(),
+ c.isPackagePrivate(),
+ c.isPrivate(),
+ c.isStatic(),
+ c.isInterface(),
+ c.isAbstract(),
+ c.isOrdinaryClass(),
+ c.isException(),
+ c.isError(),
+ c.isEnum(),
+ (c instanceof AnnotationTypeDoc),
+ c.isFinal(),
+ c.isIncluded(),
+ c.name(),
+ c.qualifiedName(),
+ c.qualifiedTypeName(),
+ c.isPrimitive());
+ if (mClassesNeedingInit != null) {
+ mClassesNeedingInit.add(new ClassNeedingInit(c, cl));
+ }
+ return cl;
+ }
+ protected void made(Object o, Object r)
+ {
+ if (mClassesNeedingInit == null) {
+ initClass((ClassDoc)o, (ClassInfo)r);
+ ((ClassInfo)r).init2();
+ }
+ }
+ ClassInfo[] all()
+ {
+ return (ClassInfo[])mCache.values().toArray(new ClassInfo[mCache.size()]);
+ }
+ };
+
+ private static MethodInfo[] getHiddenMethods(MethodDoc[] methods){
+ if (methods == null) return null;
+ ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+ int N = methods.length;
+ for (int i=0; i<N; i++) {
+ MethodInfo m = Converter.obtainMethod(methods[i]);
+ //System.out.println(m.toString() + ": ");
+ //for (TypeInfo ti : m.getTypeParameters()){
+ // if (ti.asClassInfo() != null){
+ //System.out.println(" " +ti.asClassInfo().toString());
+ //} else {
+ //System.out.println(" null");
+ //}
+ //}
+ if (m.isHidden()) {
+ out.add(m);
+ }
+ }
+ return out.toArray(new MethodInfo[out.size()]);
+ }
+
+ /**
+ * Convert MethodDoc[] into MethodInfo[]. Also filters according
+ * to the -private, -public option, because the filtering doesn't seem
+ * to be working in the ClassDoc.constructors(boolean) call.
+ */
+ private static MethodInfo[] convertMethods(MethodDoc[] methods)
+ {
+ if (methods == null) return null;
+ ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+ int N = methods.length;
+ for (int i=0; i<N; i++) {
+ MethodInfo m = Converter.obtainMethod(methods[i]);
+ //System.out.println(m.toString() + ": ");
+ //for (TypeInfo ti : m.getTypeParameters()){
+ // if (ti.asClassInfo() != null){
+ //System.out.println(" " +ti.asClassInfo().toString());
+ //} else {
+ //System.out.println(" null");
+ //}
+ //}
+ if (m.checkLevel()) {
+ out.add(m);
+ }
+ }
+ return out.toArray(new MethodInfo[out.size()]);
+ }
+
+ private static MethodInfo[] convertMethods(ConstructorDoc[] methods)
+ {
+ if (methods == null) return null;
+ ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+ int N = methods.length;
+ for (int i=0; i<N; i++) {
+ MethodInfo m = Converter.obtainMethod(methods[i]);
+ if (m.checkLevel()) {
+ out.add(m);
+ }
+ }
+ return out.toArray(new MethodInfo[out.size()]);
+ }
+
+ private static MethodInfo[] convertNonWrittenConstructors(ConstructorDoc[] methods)
+ {
+ if (methods == null) return null;
+ ArrayList<MethodInfo> out = new ArrayList<MethodInfo>();
+ int N = methods.length;
+ for (int i=0; i<N; i++) {
+ MethodInfo m = Converter.obtainMethod(methods[i]);
+ if (!m.checkLevel()) {
+ out.add(m);
+ }
+ }
+ return out.toArray(new MethodInfo[out.size()]);
+ }
+
+ private static MethodInfo obtainMethod(MethodDoc o)
+ {
+ return (MethodInfo)mMethods.obtain(o);
+ }
+ private static MethodInfo obtainMethod(ConstructorDoc o)
+ {
+ return (MethodInfo)mMethods.obtain(o);
+ }
+ private static Cache mMethods = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ if (o instanceof AnnotationTypeElementDoc) {
+ AnnotationTypeElementDoc m = (AnnotationTypeElementDoc)o;
+ MethodInfo result = new MethodInfo(
+ m.getRawCommentText(),
+ Converter.convertTypes(m.typeParameters()),
+ m.name(), m.signature(),
+ Converter.obtainClass(m.containingClass()),
+ Converter.obtainClass(m.containingClass()),
+ m.isPublic(), m.isProtected(),
+ m.isPackagePrivate(), m.isPrivate(),
+ m.isFinal(), m.isStatic(), m.isSynthetic(),
+ m.isAbstract(), m.isSynchronized(), m.isNative(), true,
+ "annotationElement",
+ m.flatSignature(),
+ Converter.obtainMethod(m.overriddenMethod()),
+ Converter.obtainType(m.returnType()),
+ Converter.convertParameters(m.parameters(), m),
+ Converter.convertClasses(m.thrownExceptions()),
+ Converter.convertSourcePosition(m.position()),
+ Converter.convertAnnotationInstances(m.annotations())
+ );
+ result.setVarargs(m.isVarArgs());
+ result.init(Converter.obtainAnnotationValue(m.defaultValue(), result));
+ return result;
+ }
+ else if (o instanceof MethodDoc) {
+ MethodDoc m = (MethodDoc)o;
+ MethodInfo result = new MethodInfo(
+ m.getRawCommentText(),
+ Converter.convertTypes(m.typeParameters()),
+ m.name(), m.signature(),
+ Converter.obtainClass(m.containingClass()),
+ Converter.obtainClass(m.containingClass()),
+ m.isPublic(), m.isProtected(),
+ m.isPackagePrivate(), m.isPrivate(),
+ m.isFinal(), m.isStatic(), m.isSynthetic(),
+ m.isAbstract(), m.isSynchronized(), m.isNative(), false,
+ "method",
+ m.flatSignature(),
+ Converter.obtainMethod(m.overriddenMethod()),
+ Converter.obtainType(m.returnType()),
+ Converter.convertParameters(m.parameters(), m),
+ Converter.convertClasses(m.thrownExceptions()),
+ Converter.convertSourcePosition(m.position()),
+ Converter.convertAnnotationInstances(m.annotations())
+ );
+ result.setVarargs(m.isVarArgs());
+ result.init(null);
+ return result;
+ }
+ else {
+ ConstructorDoc m = (ConstructorDoc)o;
+ MethodInfo result = new MethodInfo(
+ m.getRawCommentText(),
+ Converter.convertTypes(m.typeParameters()),
+ m.name(), m.signature(),
+ Converter.obtainClass(m.containingClass()),
+ Converter.obtainClass(m.containingClass()),
+ m.isPublic(), m.isProtected(),
+ m.isPackagePrivate(), m.isPrivate(),
+ m.isFinal(), m.isStatic(), m.isSynthetic(),
+ false, m.isSynchronized(), m.isNative(), false,
+ "constructor",
+ m.flatSignature(),
+ null,
+ null,
+ Converter.convertParameters(m.parameters(), m),
+ Converter.convertClasses(m.thrownExceptions()),
+ Converter.convertSourcePosition(m.position()),
+ Converter.convertAnnotationInstances(m.annotations())
+ );
+ result.setVarargs(m.isVarArgs());
+ result.init(null);
+ return result;
+ }
+ }
+ };
+
+
+ private static FieldInfo[] convertFields(FieldDoc[] fields)
+ {
+ if (fields == null) return null;
+ ArrayList<FieldInfo> out = new ArrayList<FieldInfo>();
+ int N = fields.length;
+ for (int i=0; i<N; i++) {
+ FieldInfo f = Converter.obtainField(fields[i]);
+ if (f.checkLevel()) {
+ out.add(f);
+ }
+ }
+ return out.toArray(new FieldInfo[out.size()]);
+ }
+
+ private static FieldInfo obtainField(FieldDoc o)
+ {
+ return (FieldInfo)mFields.obtain(o);
+ }
+ private static FieldInfo obtainField(ConstructorDoc o)
+ {
+ return (FieldInfo)mFields.obtain(o);
+ }
+ private static Cache mFields = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ FieldDoc f = (FieldDoc)o;
+ return new FieldInfo(f.name(),
+ Converter.obtainClass(f.containingClass()),
+ Converter.obtainClass(f.containingClass()),
+ f.isPublic(), f.isProtected(),
+ f.isPackagePrivate(), f.isPrivate(),
+ f.isFinal(), f.isStatic(), f.isTransient(), f.isVolatile(),
+ f.isSynthetic(),
+ Converter.obtainType(f.type()),
+ f.getRawCommentText(), f.constantValue(),
+ Converter.convertSourcePosition(f.position()),
+ Converter.convertAnnotationInstances(f.annotations())
+ );
+ }
+ };
+
+ private static PackageInfo obtainPackage(PackageDoc o)
+ {
+ return (PackageInfo)mPackagees.obtain(o);
+ }
+ private static Cache mPackagees = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ PackageDoc p = (PackageDoc)o;
+ return new PackageInfo(p, p.name(),
+ Converter.convertSourcePosition(p.position()));
+ }
+ };
+
+ private static TypeInfo obtainType(Type o)
+ {
+ return (TypeInfo)mTypes.obtain(o);
+ }
+ private static Cache mTypes = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ Type t = (Type)o;
+ String simpleTypeName;
+ if (t instanceof ClassDoc) {
+ simpleTypeName = ((ClassDoc)t).name();
+ } else {
+ simpleTypeName = t.simpleTypeName();
+ }
+ TypeInfo ti = new TypeInfo(t.isPrimitive(), t.dimension(),
+ simpleTypeName, t.qualifiedTypeName(),
+ Converter.obtainClass(t.asClassDoc()));
+ return ti;
+ }
+ protected void made(Object o, Object r)
+ {
+ Type t = (Type)o;
+ TypeInfo ti = (TypeInfo)r;
+ if (t.asParameterizedType() != null) {
+ ti.setTypeArguments(Converter.convertTypes(
+ t.asParameterizedType().typeArguments()));
+ }
+ else if (t instanceof ClassDoc) {
+ ti.setTypeArguments(Converter.convertTypes(((ClassDoc)t).typeParameters()));
+ }
+ else if (t.asTypeVariable() != null) {
+ ti.setBounds(null, Converter.convertTypes((t.asTypeVariable().bounds())));
+ ti.setIsTypeVariable(true);
+ }
+ else if (t.asWildcardType() != null) {
+ ti.setIsWildcard(true);
+ ti.setBounds(Converter.convertTypes(t.asWildcardType().superBounds()),
+ Converter.convertTypes(t.asWildcardType().extendsBounds()));
+ }
+ }
+ protected Object keyFor(Object o)
+ {
+ Type t = (Type)o;
+ String keyString = o.getClass().getName() + "/" + o.toString() + "/";
+ if (t.asParameterizedType() != null){
+ keyString += t.asParameterizedType().toString() +"/";
+ if (t.asParameterizedType().typeArguments() != null){
+ for(Type ty : t.asParameterizedType().typeArguments()){
+ keyString += ty.toString() + "/";
+ }
+ }
+ }else{
+ keyString += "NoParameterizedType//";
+ }
+ if (t.asTypeVariable() != null){
+ keyString += t.asTypeVariable().toString() +"/";
+ if (t.asTypeVariable().bounds() != null){
+ for(Type ty : t.asTypeVariable().bounds()){
+ keyString += ty.toString() + "/";
+ }
+ }
+ }else{
+ keyString += "NoTypeVariable//";
+ }
+ if (t.asWildcardType() != null){
+ keyString += t.asWildcardType().toString() +"/";
+ if (t.asWildcardType().superBounds() != null){
+ for(Type ty : t.asWildcardType().superBounds()){
+ keyString += ty.toString() + "/";
+ }
+ }
+ if (t.asWildcardType().extendsBounds() != null){
+ for(Type ty : t.asWildcardType().extendsBounds()){
+ keyString += ty.toString() + "/";
+ }
+ }
+ }else{
+ keyString += "NoWildCardType//";
+ }
+
+
+
+ return keyString;
+ }
+ };
+
+
+
+ private static MemberInfo obtainMember(MemberDoc o)
+ {
+ return (MemberInfo)mMembers.obtain(o);
+ }
+ private static Cache mMembers = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ if (o instanceof MethodDoc) {
+ return Converter.obtainMethod((MethodDoc)o);
+ }
+ else if (o instanceof ConstructorDoc) {
+ return Converter.obtainMethod((ConstructorDoc)o);
+ }
+ else if (o instanceof FieldDoc) {
+ return Converter.obtainField((FieldDoc)o);
+ }
+ else {
+ return null;
+ }
+ }
+ };
+
+ private static AnnotationInstanceInfo[] convertAnnotationInstances(AnnotationDesc[] orig)
+ {
+ int len = orig.length;
+ AnnotationInstanceInfo[] out = new AnnotationInstanceInfo[len];
+ for (int i=0; i<len; i++) {
+ out[i] = Converter.obtainAnnotationInstance(orig[i]);
+ }
+ return out;
+ }
+
+
+ private static AnnotationInstanceInfo obtainAnnotationInstance(AnnotationDesc o)
+ {
+ return (AnnotationInstanceInfo)mAnnotationInstances.obtain(o);
+ }
+ private static Cache mAnnotationInstances = new Cache()
+ {
+ protected Object make(Object o)
+ {
+ AnnotationDesc a = (AnnotationDesc)o;
+ ClassInfo annotationType = Converter.obtainClass(a.annotationType());
+ AnnotationDesc.ElementValuePair[] ev = a.elementValues();
+ AnnotationValueInfo[] elementValues = new AnnotationValueInfo[ev.length];
+ for (int i=0; i<ev.length; i++) {
+ elementValues[i] = obtainAnnotationValue(ev[i].value(),
+ Converter.obtainMethod(ev[i].element()));
+ }
+ return new AnnotationInstanceInfo(annotationType, elementValues);
+ }
+ };
+
+
+ private abstract static class Cache
+ {
+ void put(Object key, Object value)
+ {
+ mCache.put(key, value);
+ }
+ Object obtain(Object o)
+ {
+ if (o == null ) {
+ return null;
+ }
+ Object k = keyFor(o);
+ Object r = mCache.get(k);
+ if (r == null) {
+ r = make(o);
+ mCache.put(k, r);
+ made(o, r);
+ }
+ return r;
+ }
+ protected HashMap<Object,Object> mCache = new HashMap<Object,Object>();
+ protected abstract Object make(Object o);
+ protected void made(Object o, Object r)
+ {
+ }
+ protected Object keyFor(Object o) { return o; }
+ Object[] all() { return null; }
+ }
+
+ // annotation values
+ private static HashMap<AnnotationValue,AnnotationValueInfo> mAnnotationValues = new HashMap();
+ private static HashSet<AnnotationValue> mAnnotationValuesNeedingInit = new HashSet();
+
+ private static AnnotationValueInfo obtainAnnotationValue(AnnotationValue o, MethodInfo element)
+ {
+ if (o == null) {
+ return null;
+ }
+ AnnotationValueInfo v = mAnnotationValues.get(o);
+ if (v != null) return v;
+ v = new AnnotationValueInfo(element);
+ mAnnotationValues.put(o, v);
+ if (mAnnotationValuesNeedingInit != null) {
+ mAnnotationValuesNeedingInit.add(o);
+ } else {
+ initAnnotationValue(o, v);
+ }
+ return v;
+ }
+
+ private static void initAnnotationValue(AnnotationValue o, AnnotationValueInfo v) {
+ Object orig = o.value();
+ Object converted;
+ if (orig instanceof Type) {
+ // class literal
+ converted = Converter.obtainType((Type)orig);
+ }
+ else if (orig instanceof FieldDoc) {
+ // enum constant
+ converted = Converter.obtainField((FieldDoc)orig);
+ }
+ else if (orig instanceof AnnotationDesc) {
+ // annotation instance
+ converted = Converter.obtainAnnotationInstance((AnnotationDesc)orig);
+ }
+ else if (orig instanceof AnnotationValue[]) {
+ AnnotationValue[] old = (AnnotationValue[])orig;
+ AnnotationValueInfo[] array = new AnnotationValueInfo[old.length];
+ for (int i=0; i<array.length; i++) {
+ array[i] = Converter.obtainAnnotationValue(old[i], null);
+ }
+ converted = array;
+ }
+ else {
+ converted = orig;
+ }
+ v.init(converted);
+ }
+
+ private static void finishAnnotationValueInit()
+ {
+ int depth = 0;
+ while (mAnnotationValuesNeedingInit.size() > 0) {
+ HashSet<AnnotationValue> set = mAnnotationValuesNeedingInit;
+ mAnnotationValuesNeedingInit = new HashSet();
+ for (AnnotationValue o: set) {
+ AnnotationValueInfo v = mAnnotationValues.get(o);
+ initAnnotationValue(o, v);
+ }
+ depth++;
+ }
+ mAnnotationValuesNeedingInit = null;
+ }
+}
diff --git a/tools/droiddoc/src/DocFile.java b/tools/droiddoc/src/DocFile.java
new file mode 100644
index 000000000..f53a35ce8
--- /dev/null
+++ b/tools/droiddoc/src/DocFile.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+
+public class DocFile
+{
+ private static final Pattern LINE = Pattern.compile("(.*)[\r]?\n",
+ Pattern.MULTILINE);
+ private static final Pattern PROP = Pattern.compile("([^=]+)=(.*)");
+
+ public static String readFile(String filename)
+ {
+ try {
+ File f = new File(filename);
+ int length = (int)f.length();
+ FileReader reader = new FileReader(f);
+ char[] buf = new char[length];
+ int index = 0;
+ int amt;
+ while (true) {
+ amt = reader.read(buf, index, length-index);
+
+ if (amt < 1) {
+ break;
+ }
+
+ index += amt;
+ }
+ return new String(buf, 0, index);
+ }
+ catch (IOException e) {
+ return null;
+ }
+ }
+
+ public static void writePage(String docfile, String relative,
+ String outfile)
+ {
+ HDF hdf = DroidDoc.makeHDF();
+
+ /*
+ System.out.println("docfile='" + docfile
+ + "' relative='" + relative + "'"
+ + "' outfile='" + outfile + "'");
+ */
+
+ String filedata = readFile(docfile);
+
+ // The document is properties up until the line "@jd:body".
+ // Any blank lines are ignored.
+ int start = -1;
+ int lineno = 1;
+ Matcher lines = LINE.matcher(filedata);
+ String line = null;
+ while (lines.find()) {
+ line = lines.group(1);
+ if (line.length() > 0) {
+ if (line.equals("@jd:body")) {
+ start = lines.end();
+ break;
+ }
+ Matcher prop = PROP.matcher(line);
+ if (prop.matches()) {
+ String key = prop.group(1);
+ String value = prop.group(2);
+ hdf.setValue(key, value);
+ } else {
+ break;
+ }
+ }
+ lineno++;
+ }
+ if (start < 0) {
+ System.err.println(docfile + ":" + lineno + ": error parsing docfile");
+ if (line != null) {
+ System.err.println(docfile + ":" + lineno + ":" + line);
+ }
+ System.exit(1);
+ }
+
+ // if they asked to only be for a certain template, maybe skip it
+ String fromTemplate = hdf.getValue("template.which", "");
+ String fromPage = hdf.getValue("page.onlyfortemplate", "");
+ if (!"".equals(fromPage) && !fromTemplate.equals(fromPage)) {
+ return;
+ }
+
+ // and the actual text after that
+ String commentText = filedata.substring(start);
+
+ Comment comment = new Comment(commentText, null,
+ new SourcePositionInfo(docfile, lineno, 1));
+ TagInfo[] tags = comment.tags();
+
+ TagInfo.makeHDF(hdf, "root.descr", tags);
+
+ hdf.setValue("commentText", commentText);
+
+ if (outfile.indexOf("sdk/") != -1) {
+ hdf.setValue("sdk", "true");
+ if (outfile.indexOf("index.html") != -1) {
+ ClearPage.write(hdf, "sdkpage.cs", outfile);
+ } else {
+ ClearPage.write(hdf, "docpage.cs", outfile);
+ }
+ } else if (outfile.indexOf("guide/") != -1){
+ hdf.setValue("guide", "true");
+ ClearPage.write(hdf, "docpage.cs", outfile);
+ } else if (outfile.indexOf("publish/") != -1){
+ hdf.setValue("publish", "true");
+ ClearPage.write(hdf, "docpage.cs", outfile);
+ } else {
+ ClearPage.write(hdf, "nosidenavpage.cs", outfile);
+ }
+ }
+
+}
diff --git a/tools/droiddoc/src/DocInfo.java b/tools/droiddoc/src/DocInfo.java
new file mode 100644
index 000000000..2530dc249
--- /dev/null
+++ b/tools/droiddoc/src/DocInfo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class DocInfo
+{
+ public DocInfo(String rawCommentText, SourcePositionInfo sp)
+ {
+ mRawCommentText = rawCommentText;
+ mPosition = sp;
+ }
+
+ public boolean isHidden()
+ {
+ return comment().isHidden();
+ }
+
+ public boolean isDocOnly() {
+ return comment().isDocOnly();
+ }
+
+ public String getRawCommentText()
+ {
+ return mRawCommentText;
+ }
+
+ public Comment comment()
+ {
+ if (mComment == null) {
+ mComment = new Comment(mRawCommentText, parent(), mPosition);
+ }
+ return mComment;
+ }
+
+ public SourcePositionInfo position()
+ {
+ return mPosition;
+ }
+
+ public abstract ContainerInfo parent();
+
+ private String mRawCommentText;
+ Comment mComment;
+ SourcePositionInfo mPosition;
+}
+
diff --git a/tools/droiddoc/src/DroidDoc.java b/tools/droiddoc/src/DroidDoc.java
new file mode 100644
index 000000000..f664c416d
--- /dev/null
+++ b/tools/droiddoc/src/DroidDoc.java
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.sun.javadoc.*;
+
+import org.clearsilver.HDF;
+
+import java.util.*;
+import java.io.*;
+import java.lang.reflect.Proxy;
+import java.lang.reflect.Array;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class DroidDoc
+{
+ private static final String SDK_CONSTANT_ANNOTATION = "android.annotation.SdkConstant";
+ private static final String SDK_CONSTANT_TYPE_ACTIVITY_ACTION = "android.annotation.SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_BROADCAST_ACTION = "android.annotation.SdkConstant.SdkConstantType.BROADCAST_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_SERVICE_ACTION = "android.annotation.SdkConstant.SdkConstantType.SERVICE_INTENT_ACTION";
+ private static final String SDK_CONSTANT_TYPE_CATEGORY = "android.annotation.SdkConstant.SdkConstantType.INTENT_CATEGORY";
+ private static final String SDK_WIDGET_ANNOTATION = "android.annotation.Widget";
+ private static final String SDK_LAYOUT_ANNOTATION = "android.annotation.Layout";
+
+ private static final int TYPE_NONE = 0;
+ private static final int TYPE_WIDGET = 1;
+ private static final int TYPE_LAYOUT = 2;
+ private static final int TYPE_LAYOUT_PARAM = 3;
+
+ public static final int SHOW_PUBLIC = 0x00000001;
+ public static final int SHOW_PROTECTED = 0x00000003;
+ public static final int SHOW_PACKAGE = 0x00000007;
+ public static final int SHOW_PRIVATE = 0x0000000f;
+ public static final int SHOW_HIDDEN = 0x0000001f;
+
+ public static int showLevel = SHOW_PROTECTED;
+
+ public static final String javadocDir = "reference/";
+ public static String htmlExtension;
+
+ public static RootDoc root;
+ public static ArrayList<String[]> mHDFData = new ArrayList<String[]>();
+ public static Map<Character,String> escapeChars = new HashMap<Character,String>();
+ public static String title = "";
+
+ public static boolean checkLevel(int level)
+ {
+ return (showLevel & level) == level;
+ }
+
+ public static boolean checkLevel(boolean pub, boolean prot, boolean pkgp,
+ boolean priv, boolean hidden)
+ {
+ int level = 0;
+ if (hidden && !checkLevel(SHOW_HIDDEN)) {
+ return false;
+ }
+ if (pub && checkLevel(SHOW_PUBLIC)) {
+ return true;
+ }
+ if (prot && checkLevel(SHOW_PROTECTED)) {
+ return true;
+ }
+ if (pkgp && checkLevel(SHOW_PACKAGE)) {
+ return true;
+ }
+ if (priv && checkLevel(SHOW_PRIVATE)) {
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean start(RootDoc r)
+ {
+ String keepListFile = null;
+ String proofreadFile = null;
+ String todoFile = null;
+ String sdkValuePath = null;
+ ArrayList<SampleCode> sampleCodes = new ArrayList<SampleCode>();
+ String stubsDir = null;
+ //Create the dependency graph for the stubs directory
+ boolean apiXML = false;
+ String apiFile = null;
+ String debugStubsFile = "";
+ HashSet<String> stubPackages = null;
+
+ root = r;
+
+ String[][] options = r.options();
+ for (String[] a: options) {
+ if (a[0].equals("-d")) {
+ ClearPage.outputDir = a[1];
+ }
+ else if (a[0].equals("-templatedir")) {
+ ClearPage.addTemplateDir(a[1]);
+ }
+ else if (a[0].equals("-hdf")) {
+ mHDFData.add(new String[] {a[1], a[2]});
+ }
+ else if (a[0].equals("-toroot")) {
+ ClearPage.toroot = a[1];
+ }
+ else if (a[0].equals("-samplecode")) {
+ sampleCodes.add(new SampleCode(a[1], a[2], a[3]));
+ }
+ else if (a[0].equals("-htmldir")) {
+ ClearPage.htmlDir = a[1];
+ }
+ else if (a[0].equals("-title")) {
+ DroidDoc.title = a[1];
+ }
+ else if (a[0].equals("-werror")) {
+ Errors.setWarningsAreErrors(true);
+ }
+ else if (a[0].equals("-error") || a[0].equals("-warning")
+ || a[0].equals("-hide")) {
+ try {
+ int level = -1;
+ if (a[0].equals("-error")) {
+ level = Errors.ERROR;
+ }
+ else if (a[0].equals("-warning")) {
+ level = Errors.WARNING;
+ }
+ else if (a[0].equals("-hide")) {
+ level = Errors.HIDDEN;
+ }
+ Errors.setErrorLevel(Integer.parseInt(a[1]), level);
+ }
+ catch (NumberFormatException e) {
+ // already printed below
+ return false;
+ }
+ }
+ else if (a[0].equals("-keeplist")) {
+ keepListFile = a[1];
+ }
+ else if (a[0].equals("-proofread")) {
+ proofreadFile = a[1];
+ }
+ else if (a[0].equals("-todo")) {
+ todoFile = a[1];
+ }
+ else if (a[0].equals("-public")) {
+ showLevel = SHOW_PUBLIC;
+ }
+ else if (a[0].equals("-protected")) {
+ showLevel = SHOW_PROTECTED;
+ }
+ else if (a[0].equals("-package")) {
+ showLevel = SHOW_PACKAGE;
+ }
+ else if (a[0].equals("-private")) {
+ showLevel = SHOW_PRIVATE;
+ }
+ else if (a[0].equals("-hidden")) {
+ showLevel = SHOW_HIDDEN;
+ }
+ else if (a[0].equals("-stubs")) {
+ stubsDir = a[1];
+ }
+ else if (a[0].equals("-stubpackages")) {
+ stubPackages = new HashSet();
+ for (String pkg: a[1].split(":")) {
+ stubPackages.add(pkg);
+ }
+ }
+ else if (a[0].equals("-sdkvalues")) {
+ sdkValuePath = a[1];
+ }
+ else if (a[0].equals("-apixml")) {
+ apiXML = true;
+ apiFile = a[1];
+ }
+ }
+
+ // read some prefs from the template
+ if (!readTemplateSettings()) {
+ return false;
+ }
+
+ // Set up the data structures
+ Converter.makeInfo(r);
+
+ // Files for proofreading
+ if (proofreadFile != null) {
+ Proofread.initProofread(proofreadFile);
+ }
+ if (todoFile != null) {
+ TodoFile.writeTodoFile(todoFile);
+ }
+
+ // HTML Pages
+ if (ClearPage.htmlDir != null) {
+ writeHTMLPages();
+ }
+
+ // Navigation tree
+ NavTree.writeNavTree(javadocDir);
+
+ // Packages Pages
+ writePackages(javadocDir
+ + (ClearPage.htmlDir!=null
+ ? "packages" + htmlExtension
+ : "index" + htmlExtension));
+
+ // Classes
+ writeClassLists();
+ writeClasses();
+ writeHierarchy();
+ // writeKeywords();
+
+ // Lists for JavaScript
+ writeLists();
+ if (keepListFile != null) {
+ writeKeepList(keepListFile);
+ }
+
+ // Sample Code
+ for (SampleCode sc: sampleCodes) {
+ sc.write();
+ }
+
+ // Index page
+ writeIndex();
+
+ Proofread.finishProofread(proofreadFile);
+
+ // Stubs
+ if (stubsDir != null) {
+ Stubs.writeStubs(stubsDir, apiXML, apiFile, stubPackages);
+ }
+
+ if (sdkValuePath != null) {
+ writeSdkValues(sdkValuePath);
+ }
+
+ Errors.printErrors();
+ return !Errors.hadError;
+ }
+
+ private static void writeIndex() {
+ HDF data = makeHDF();
+ ClearPage.write(data, "index.cs", javadocDir + "index" + htmlExtension);
+ }
+
+ private static boolean readTemplateSettings()
+ {
+ HDF data = makeHDF();
+ htmlExtension = data.getValue("template.extension", ".html");
+ int i=0;
+ while (true) {
+ String k = data.getValue("template.escape." + i + ".key", "");
+ String v = data.getValue("template.escape." + i + ".value", "");
+ if ("".equals(k)) {
+ break;
+ }
+ if (k.length() != 1) {
+ System.err.println("template.escape." + i + ".key must have a length of 1: " + k);
+ return false;
+ }
+ escapeChars.put(k.charAt(0), v);
+ i++;
+ }
+ return true;
+ }
+
+ public static String escape(String s) {
+ if (escapeChars.size() == 0) {
+ return s;
+ }
+ StringBuffer b = null;
+ int begin = 0;
+ final int N = s.length();
+ for (int i=0; i<N; i++) {
+ char c = s.charAt(i);
+ String mapped = escapeChars.get(c);
+ if (mapped != null) {
+ if (b == null) {
+ b = new StringBuffer(s.length() + mapped.length());
+ }
+ if (begin != i) {
+ b.append(s.substring(begin, i));
+ }
+ b.append(mapped);
+ begin = i+1;
+ }
+ }
+ if (b != null) {
+ if (begin != N) {
+ b.append(s.substring(begin, N));
+ }
+ return b.toString();
+ }
+ return s;
+ }
+
+ public static void setPageTitle(HDF data, String title)
+ {
+ String s = title;
+ if (DroidDoc.title.length() > 0) {
+ s += " - " + DroidDoc.title;
+ }
+ data.setValue("page.title", s);
+ }
+
+ public static LanguageVersion languageVersion()
+ {
+ return LanguageVersion.JAVA_1_5;
+ }
+
+ public static int optionLength(String option)
+ {
+ if (option.equals("-d")) {
+ return 2;
+ }
+ if (option.equals("-templatedir")) {
+ return 2;
+ }
+ if (option.equals("-hdf")) {
+ return 3;
+ }
+ if (option.equals("-toroot")) {
+ return 2;
+ }
+ if (option.equals("-samplecode")) {
+ return 4;
+ }
+ if (option.equals("-htmldir")) {
+ return 2;
+ }
+ if (option.equals("-title")) {
+ return 2;
+ }
+ if (option.equals("-werror")) {
+ return 1;
+ }
+ if (option.equals("-hide")) {
+ return 2;
+ }
+ if (option.equals("-warning")) {
+ return 2;
+ }
+ if (option.equals("-error")) {
+ return 2;
+ }
+ if (option.equals("-keeplist")) {
+ return 2;
+ }
+ if (option.equals("-proofread")) {
+ return 2;
+ }
+ if (option.equals("-todo")) {
+ return 2;
+ }
+ if (option.equals("-public")) {
+ return 1;
+ }
+ if (option.equals("-protected")) {
+ return 1;
+ }
+ if (option.equals("-package")) {
+ return 1;
+ }
+ if (option.equals("-private")) {
+ return 1;
+ }
+ if (option.equals("-hidden")) {
+ return 1;
+ }
+ if (option.equals("-stubs")) {
+ return 2;
+ }
+ if (option.equals("-stubpackages")) {
+ return 2;
+ }
+ if (option.equals("-sdkvalues")) {
+ return 2;
+ }
+ if (option.equals("-apixml")) {
+ return 2;
+ }
+ return 0;
+ }
+
+ public static boolean validOptions(String[][] options, DocErrorReporter r)
+ {
+ for (String[] a: options) {
+ if (a[0].equals("-error") || a[0].equals("-warning")
+ || a[0].equals("-hide")) {
+ try {
+ Integer.parseInt(a[1]);
+ }
+ catch (NumberFormatException e) {
+ r.printError("bad -" + a[0] + " value must be a number: "
+ + a[1]);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public static HDF makeHDF()
+ {
+ HDF data = new HDF();
+
+ for (String[] p: mHDFData) {
+ data.setValue(p[0], p[1]);
+ }
+
+ try {
+ for (String p: ClearPage.hdfFiles) {
+ data.readFile(p);
+ }
+ }
+ catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return data;
+ }
+
+ public static HDF makePackageHDF()
+ {
+ HDF data = makeHDF();
+ ClassInfo[] classes = Converter.rootClasses();
+
+ SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
+ for (ClassInfo cl: classes) {
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ int i = 0;
+ for (String s: sorted.keySet()) {
+ PackageInfo pkg = sorted.get(s);
+
+ if (pkg.isHidden()) {
+ continue;
+ }
+ Boolean allHidden = true;
+ int pass = 0;
+ ClassInfo[] classesToCheck = null;
+ while (pass < 5 ) {
+ switch(pass) {
+ case 0:
+ classesToCheck = pkg.ordinaryClasses();
+ break;
+ case 1:
+ classesToCheck = pkg.enums();
+ break;
+ case 2:
+ classesToCheck = pkg.errors();
+ break;
+ case 3:
+ classesToCheck = pkg.exceptions();
+ break;
+ case 4:
+ classesToCheck = pkg.interfaces();
+ break;
+ default:
+ System.err.println("Error reading package: " + pkg.name());
+ break;
+ }
+ for (ClassInfo cl : classesToCheck) {
+ if (!cl.isHidden()) {
+ allHidden = false;
+ break;
+ }
+ }
+ if (!allHidden) {
+ break;
+ }
+ pass++;
+ }
+ if (allHidden) {
+ continue;
+ }
+
+ data.setValue("reference", "true");
+ data.setValue("docs.packages." + i + ".name", s);
+ data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+ TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
+ pkg.firstSentenceTags());
+ i++;
+ }
+
+ return data;
+ }
+
+ public static void writeDirectory(File dir, String relative)
+ {
+ File[] files = dir.listFiles();
+ int i, count = files.length;
+ for (i=0; i<count; i++) {
+ File f = files[i];
+ if (f.isFile()) {
+ String templ = relative + f.getName();
+ int len = templ.length();
+ if (len > 3 && ".cs".equals(templ.substring(len-3))) {
+ HDF data = makeHDF();
+ String filename = templ.substring(0,len-3) + htmlExtension;
+ ClearPage.write(data, templ, filename);
+ }
+ else if (len > 3 && ".jd".equals(templ.substring(len-3))) {
+ String filename = templ.substring(0,len-3) + htmlExtension;
+ DocFile.writePage(f.getAbsolutePath(), relative, filename);
+ }
+ else {
+ ClearPage.copyFile(f, templ);
+ }
+ }
+ else if (f.isDirectory()) {
+ writeDirectory(f, relative + f.getName() + "/");
+ }
+ }
+ }
+
+ public static void writeHTMLPages()
+ {
+ File f = new File(ClearPage.htmlDir);
+ if (!f.isDirectory()) {
+ System.err.println("htmlDir not a directory: " + ClearPage.htmlDir);
+ }
+ writeDirectory(f, "");
+ }
+
+ public static void writeLists()
+ {
+ HDF data = makeHDF();
+
+ ClassInfo[] classes = Converter.rootClasses();
+
+ SortedMap<String, Object> sorted = new TreeMap<String, Object>();
+ for (ClassInfo cl: classes) {
+ if (cl.isHidden()) {
+ continue;
+ }
+ sorted.put(cl.qualifiedName(), cl);
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ int i = 0;
+ for (String s: sorted.keySet()) {
+ data.setValue("docs.pages." + i + ".id" , ""+i);
+ data.setValue("docs.pages." + i + ".label" , s);
+
+ Object o = sorted.get(s);
+ if (o instanceof PackageInfo) {
+ PackageInfo pkg = (PackageInfo)o;
+ data.setValue("docs.pages." + i + ".link" , pkg.htmlPage());
+ data.setValue("docs.pages." + i + ".type" , "package");
+ }
+ else if (o instanceof ClassInfo) {
+ ClassInfo cl = (ClassInfo)o;
+ data.setValue("docs.pages." + i + ".link" , cl.htmlPage());
+ data.setValue("docs.pages." + i + ".type" , "class");
+ }
+ i++;
+ }
+
+ ClearPage.write(data, "lists.cs", javadocDir + "lists.js");
+ }
+
+ public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable) {
+ if (!notStrippable.add(cl)) {
+ // slight optimization: if it already contains cl, it already contains
+ // all of cl's parents
+ return;
+ }
+ ClassInfo supr = cl.superclass();
+ if (supr != null) {
+ cantStripThis(supr, notStrippable);
+ }
+ for (ClassInfo iface: cl.interfaces()) {
+ cantStripThis(iface, notStrippable);
+ }
+ }
+
+ private static String getPrintableName(ClassInfo cl) {
+ ClassInfo containingClass = cl.containingClass();
+ if (containingClass != null) {
+ // This is an inner class.
+ String baseName = cl.name();
+ baseName = baseName.substring(baseName.lastIndexOf('.') + 1);
+ return getPrintableName(containingClass) + '$' + baseName;
+ }
+ return cl.qualifiedName();
+ }
+
+ /**
+ * Writes the list of classes that must be present in order to
+ * provide the non-hidden APIs known to javadoc.
+ *
+ * @param filename the path to the file to write the list to
+ */
+ public static void writeKeepList(String filename) {
+ HashSet<ClassInfo> notStrippable = new HashSet<ClassInfo>();
+ ClassInfo[] all = Converter.allClasses();
+ Arrays.sort(all); // just to make the file a little more readable
+
+ // If a class is public and not hidden, then it and everything it derives
+ // from cannot be stripped. Otherwise we can strip it.
+ for (ClassInfo cl: all) {
+ if (cl.isPublic() && !cl.isHidden()) {
+ cantStripThis(cl, notStrippable);
+ }
+ }
+ PrintStream stream = null;
+ try {
+ stream = new PrintStream(filename);
+ for (ClassInfo cl: notStrippable) {
+ stream.println(getPrintableName(cl));
+ }
+ }
+ catch (FileNotFoundException e) {
+ System.err.println("error writing file: " + filename);
+ }
+ finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ private static PackageInfo[] sVisiblePackages = null;
+ public static PackageInfo[] choosePackages() {
+ if (sVisiblePackages != null) {
+ return sVisiblePackages;
+ }
+
+ ClassInfo[] classes = Converter.rootClasses();
+ SortedMap<String, PackageInfo> sorted = new TreeMap<String, PackageInfo>();
+ for (ClassInfo cl: classes) {
+ PackageInfo pkg = cl.containingPackage();
+ String name;
+ if (pkg == null) {
+ name = "";
+ } else {
+ name = pkg.name();
+ }
+ sorted.put(name, pkg);
+ }
+
+ ArrayList<PackageInfo> result = new ArrayList();
+
+ for (String s: sorted.keySet()) {
+ PackageInfo pkg = sorted.get(s);
+
+ if (pkg.isHidden()) {
+ continue;
+ }
+ Boolean allHidden = true;
+ int pass = 0;
+ ClassInfo[] classesToCheck = null;
+ while (pass < 5 ) {
+ switch(pass) {
+ case 0:
+ classesToCheck = pkg.ordinaryClasses();
+ break;
+ case 1:
+ classesToCheck = pkg.enums();
+ break;
+ case 2:
+ classesToCheck = pkg.errors();
+ break;
+ case 3:
+ classesToCheck = pkg.exceptions();
+ break;
+ case 4:
+ classesToCheck = pkg.interfaces();
+ break;
+ default:
+ System.err.println("Error reading package: " + pkg.name());
+ break;
+ }
+ for (ClassInfo cl : classesToCheck) {
+ if (!cl.isHidden()) {
+ allHidden = false;
+ break;
+ }
+ }
+ if (!allHidden) {
+ break;
+ }
+ pass++;
+ }
+ if (allHidden) {
+ continue;
+ }
+
+ result.add(pkg);
+ }
+
+ sVisiblePackages = result.toArray(new PackageInfo[result.size()]);
+ return sVisiblePackages;
+ }
+
+ public static void writePackages(String filename)
+ {
+ HDF data = makePackageHDF();
+
+ int i = 0;
+ for (PackageInfo pkg: choosePackages()) {
+ writePackage(pkg);
+
+ data.setValue("docs.packages." + i + ".name", pkg.name());
+ data.setValue("docs.packages." + i + ".link", pkg.htmlPage());
+ TagInfo.makeHDF(data, "docs.packages." + i + ".shortDescr",
+ pkg.firstSentenceTags());
+
+ i++;
+ }
+
+ setPageTitle(data, "Package Index");
+
+ TagInfo.makeHDF(data, "root.descr",
+ Converter.convertTags(root.inlineTags(), null));
+
+ ClearPage.write(data, "packages.cs", filename);
+ ClearPage.write(data, "package-list.cs", javadocDir + "package-list");
+
+ Proofread.writePackages(filename,
+ Converter.convertTags(root.inlineTags(), null));
+ }
+
+ public static void writePackage(PackageInfo pkg)
+ {
+ // these this and the description are in the same directory,
+ // so it's okay
+ HDF data = makePackageHDF();
+
+ String name = pkg.name();
+
+ data.setValue("package.name", name);
+ data.setValue("package.descr", "...description...");
+
+ makeClassListHDF(data, "package.interfaces",
+ ClassInfo.sortByName(pkg.interfaces()));
+ makeClassListHDF(data, "package.classes",
+ ClassInfo.sortByName(pkg.ordinaryClasses()));
+ makeClassListHDF(data, "package.enums",
+ ClassInfo.sortByName(pkg.enums()));
+ makeClassListHDF(data, "package.exceptions",
+ ClassInfo.sortByName(pkg.exceptions()));
+ makeClassListHDF(data, "package.errors",
+ ClassInfo.sortByName(pkg.errors()));
+ TagInfo.makeHDF(data, "package.shortDescr",
+ pkg.firstSentenceTags());
+ TagInfo.makeHDF(data, "package.descr", pkg.inlineTags());
+
+ String filename = pkg.htmlPage();
+ setPageTitle(data, name);
+ ClearPage.write(data, "package.cs", filename);
+
+ filename = pkg.fullDescriptionHtmlPage();
+ setPageTitle(data, name + " Details");
+ ClearPage.write(data, "package-descr.cs", filename);
+
+ Proofread.writePackage(filename, pkg.inlineTags());
+ }
+
+ public static void writeClassLists()
+ {
+ int i;
+ HDF data = makePackageHDF();
+
+ ClassInfo[] classes = PackageInfo.filterHidden(
+ Converter.convertClasses(root.classes()));
+ if (classes.length == 0) {
+ return ;
+ }
+
+ Sorter[] sorted = new Sorter[classes.length];
+ for (i=0; i<sorted.length; i++) {
+ ClassInfo cl = classes[i];
+ String name = cl.name();
+ sorted[i] = new Sorter(name, cl);
+ }
+
+ Arrays.sort(sorted);
+
+ // make a pass and resolve ones that have the same name
+ int firstMatch = 0;
+ String lastName = sorted[0].label;
+ for (i=1; i<sorted.length; i++) {
+ String s = sorted[i].label;
+ if (!lastName.equals(s)) {
+ if (firstMatch != i-1) {
+ // there were duplicates
+ for (int j=firstMatch; j<i; j++) {
+ PackageInfo pkg = ((ClassInfo)sorted[j].data).containingPackage();
+ if (pkg != null) {
+ sorted[j].label = sorted[j].label + " (" + pkg.name() + ")";
+ }
+ }
+ }
+ firstMatch = i;
+ lastName = s;
+ }
+ }
+
+ // and sort again
+ Arrays.sort(sorted);
+
+ for (i=0; i<sorted.length; i++) {
+ String s = sorted[i].label;
+ ClassInfo cl = (ClassInfo)sorted[i].data;
+ char first = Character.toUpperCase(s.charAt(0));
+ cl.makeShortDescrHDF(data, "docs.classes." + first + '.' + i);
+ }
+
+ setPageTitle(data, "Class Index");
+ ClearPage.write(data, "classes.cs", javadocDir + "classes" + htmlExtension);
+ }
+
+ // we use the word keywords because "index" means something else in html land
+ // the user only ever sees the word index
+/* public static void writeKeywords()
+ {
+ ArrayList<KeywordEntry> keywords = new ArrayList<KeywordEntry>();
+
+ ClassInfo[] classes = PackageInfo.filterHidden(Converter.convertClasses(root.classes()));
+
+ for (ClassInfo cl: classes) {
+ cl.makeKeywordEntries(keywords);
+ }
+
+ HDF data = makeHDF();
+
+ Collections.sort(keywords);
+
+ int i=0;
+ for (KeywordEntry entry: keywords) {
+ String base = "keywords." + entry.firstChar() + "." + i;
+ entry.makeHDF(data, base);
+ i++;
+ }
+
+ setPageTitle(data, "Index");
+ ClearPage.write(data, "keywords.cs", javadocDir + "keywords" + htmlExtension);
+ } */
+
+ public static void writeHierarchy()
+ {
+ ClassInfo[] classes = Converter.rootClasses();
+ ArrayList<ClassInfo> info = new ArrayList<ClassInfo>();
+ for (ClassInfo cl: classes) {
+ if (!cl.isHidden()) {
+ info.add(cl);
+ }
+ }
+ HDF data = makePackageHDF();
+ Hierarchy.makeHierarchy(data, info.toArray(new ClassInfo[info.size()]));
+ setPageTitle(data, "Class Hierarchy");
+ ClearPage.write(data, "hierarchy.cs", javadocDir + "hierarchy" + htmlExtension);
+ }
+
+ public static void writeClasses()
+ {
+ ClassInfo[] classes = Converter.rootClasses();
+
+ for (ClassInfo cl: classes) {
+ HDF data = makePackageHDF();
+ if (!cl.isHidden()) {
+ writeClass(cl, data);
+ }
+ }
+ }
+
+ public static void writeClass(ClassInfo cl, HDF data)
+ {
+ cl.makeHDF(data);
+
+ setPageTitle(data, cl.name());
+ ClearPage.write(data, "class.cs", cl.htmlPage());
+
+ Proofread.writeClass(cl.htmlPage(), cl);
+ }
+
+ public static void makeClassListHDF(HDF data, String base,
+ ClassInfo[] classes)
+ {
+ for (int i=0; i<classes.length; i++) {
+ ClassInfo cl = classes[i];
+ if (!cl.isHidden()) {
+ cl.makeShortDescrHDF(data, base + "." + i);
+ }
+ }
+ }
+
+ public static String linkTarget(String source, String target)
+ {
+ String[] src = source.split("/");
+ String[] tgt = target.split("/");
+
+ int srclen = src.length;
+ int tgtlen = tgt.length;
+
+ int same = 0;
+ while (same < (srclen-1)
+ && same < (tgtlen-1)
+ && (src[same].equals(tgt[same]))) {
+ same++;
+ }
+
+ String s = "";
+
+ int up = srclen-same-1;
+ for (int i=0; i<up; i++) {
+ s += "../";
+ }
+
+
+ int N = tgtlen-1;
+ for (int i=same; i<N; i++) {
+ s += tgt[i] + '/';
+ }
+ s += tgt[tgtlen-1];
+
+ return s;
+ }
+
+ /**
+ * Returns true if the given element has an @hide annotation.
+ */
+ private static boolean hasHideAnnotation(Doc doc) {
+ return doc.getRawCommentText().indexOf("@hide") != -1;
+ }
+
+ /**
+ * Returns true if the given element is hidden.
+ */
+ private static boolean isHidden(Doc doc) {
+ // Methods, fields, constructors.
+ if (doc instanceof MemberDoc) {
+ return hasHideAnnotation(doc);
+ }
+
+ // Classes, interfaces, enums, annotation types.
+ if (doc instanceof ClassDoc) {
+ ClassDoc classDoc = (ClassDoc) doc;
+
+ // Check the containing package.
+ if (hasHideAnnotation(classDoc.containingPackage())) {
+ return true;
+ }
+
+ // Check the class doc and containing class docs if this is a
+ // nested class.
+ ClassDoc current = classDoc;
+ do {
+ if (hasHideAnnotation(current)) {
+ return true;
+ }
+
+ current = current.containingClass();
+ } while (current != null);
+ }
+
+ return false;
+ }
+
+ /**
+ * Filters out hidden elements.
+ */
+ private static Object filterHidden(Object o, Class<?> expected) {
+ if (o == null) {
+ return null;
+ }
+
+ Class type = o.getClass();
+ if (type.getName().startsWith("com.sun.")) {
+ // TODO: Implement interfaces from superclasses, too.
+ return Proxy.newProxyInstance(type.getClassLoader(),
+ type.getInterfaces(), new HideHandler(o));
+ } else if (o instanceof Object[]) {
+ Class<?> componentType = expected.getComponentType();
+ Object[] array = (Object[]) o;
+ List<Object> list = new ArrayList<Object>(array.length);
+ for (Object entry : array) {
+ if ((entry instanceof Doc) && isHidden((Doc) entry)) {
+ continue;
+ }
+ list.add(filterHidden(entry, componentType));
+ }
+ return list.toArray(
+ (Object[]) Array.newInstance(componentType, list.size()));
+ } else {
+ return o;
+ }
+ }
+
+ /**
+ * Filters hidden elements out of method return values.
+ */
+ private static class HideHandler implements InvocationHandler {
+
+ private final Object target;
+
+ public HideHandler(Object target) {
+ this.target = target;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args)
+ throws Throwable {
+ String methodName = method.getName();
+ if (args != null) {
+ if (methodName.equals("compareTo") ||
+ methodName.equals("equals") ||
+ methodName.equals("overrides") ||
+ methodName.equals("subclassOf")) {
+ args[0] = unwrap(args[0]);
+ }
+ }
+
+ if (methodName.equals("getRawCommentText")) {
+ return filterComment((String) method.invoke(target, args));
+ }
+
+ // escape "&" in disjunctive types.
+ if (proxy instanceof Type && methodName.equals("toString")) {
+ return ((String) method.invoke(target, args))
+ .replace("&", "&amp;");
+ }
+
+ try {
+ return filterHidden(method.invoke(target, args),
+ method.getReturnType());
+ } catch (InvocationTargetException e) {
+ throw e.getTargetException();
+ }
+ }
+
+ private String filterComment(String s) {
+ if (s == null) {
+ return null;
+ }
+
+ s = s.trim();
+
+ // Work around off by one error
+ while (s.length() >= 5
+ && s.charAt(s.length() - 5) == '{') {
+ s += "&nbsp;";
+ }
+
+ return s;
+ }
+
+ private static Object unwrap(Object proxy) {
+ if (proxy instanceof Proxy)
+ return ((HideHandler)Proxy.getInvocationHandler(proxy)).target;
+ return proxy;
+ }
+ }
+
+ public static String scope(Scoped scoped) {
+ if (scoped.isPublic()) {
+ return "public";
+ }
+ else if (scoped.isProtected()) {
+ return "protected";
+ }
+ else if (scoped.isPackagePrivate()) {
+ return "";
+ }
+ else if (scoped.isPrivate()) {
+ return "private";
+ }
+ else {
+ throw new RuntimeException("invalid scope for object " + scoped);
+ }
+ }
+
+ /**
+ * Collect the values used by the Dev tools and write them in files packaged with the SDK
+ * @param output the ouput directory for the files.
+ */
+ private static void writeSdkValues(String output) {
+ ArrayList<String> activityActions = new ArrayList<String>();
+ ArrayList<String> broadcastActions = new ArrayList<String>();
+ ArrayList<String> serviceActions = new ArrayList<String>();
+ ArrayList<String> categories = new ArrayList<String>();
+
+ ArrayList<ClassInfo> layouts = new ArrayList<ClassInfo>();
+ ArrayList<ClassInfo> widgets = new ArrayList<ClassInfo>();
+ ArrayList<ClassInfo> layoutParams = new ArrayList<ClassInfo>();
+
+ ClassInfo[] classes = Converter.allClasses();
+
+ // Go through all the fields of all the classes, looking SDK stuff.
+ for (ClassInfo clazz : classes) {
+
+ // first check constant fields for the SdkConstant annotation.
+ FieldInfo[] fields = clazz.allSelfFields();
+ for (FieldInfo field : fields) {
+ Object cValue = field.constantValue();
+ if (cValue != null) {
+ AnnotationInstanceInfo[] annotations = field.annotations();
+ if (annotations.length > 0) {
+ for (AnnotationInstanceInfo annotation : annotations) {
+ if (SDK_CONSTANT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ AnnotationValueInfo[] values = annotation.elementValues();
+ if (values.length > 0) {
+ String type = values[0].valueString();
+ if (SDK_CONSTANT_TYPE_ACTIVITY_ACTION.equals(type)) {
+ activityActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_BROADCAST_ACTION.equals(type)) {
+ broadcastActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_SERVICE_ACTION.equals(type)) {
+ serviceActions.add(cValue.toString());
+ } else if (SDK_CONSTANT_TYPE_CATEGORY.equals(type)) {
+ categories.add(cValue.toString());
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // Now check the class for @Widget or if its in the android.widget package
+ // (unless the class is hidden or abstract, or non public)
+ if (clazz.isHidden() == false && clazz.isPublic() && clazz.isAbstract() == false) {
+ boolean annotated = false;
+ AnnotationInstanceInfo[] annotations = clazz.annotations();
+ if (annotations.length > 0) {
+ for (AnnotationInstanceInfo annotation : annotations) {
+ if (SDK_WIDGET_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ widgets.add(clazz);
+ annotated = true;
+ break;
+ } else if (SDK_LAYOUT_ANNOTATION.equals(annotation.type().qualifiedName())) {
+ layouts.add(clazz);
+ annotated = true;
+ break;
+ }
+ }
+ }
+
+ if (annotated == false) {
+ // lets check if this is inside android.widget
+ PackageInfo pckg = clazz.containingPackage();
+ String packageName = pckg.name();
+ if ("android.widget".equals(packageName) ||
+ "android.view".equals(packageName)) {
+ // now we check what this class inherits either from android.view.ViewGroup
+ // or android.view.View, or android.view.ViewGroup.LayoutParams
+ int type = checkInheritance(clazz);
+ switch (type) {
+ case TYPE_WIDGET:
+ widgets.add(clazz);
+ break;
+ case TYPE_LAYOUT:
+ layouts.add(clazz);
+ break;
+ case TYPE_LAYOUT_PARAM:
+ layoutParams.add(clazz);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ // now write the files, whether or not the list are empty.
+ // the SDK built requires those files to be present.
+
+ Collections.sort(activityActions);
+ writeValues(output + "/activity_actions.txt", activityActions);
+
+ Collections.sort(broadcastActions);
+ writeValues(output + "/broadcast_actions.txt", broadcastActions);
+
+ Collections.sort(serviceActions);
+ writeValues(output + "/service_actions.txt", serviceActions);
+
+ Collections.sort(categories);
+ writeValues(output + "/categories.txt", categories);
+
+ // before writing the list of classes, we do some checks, to make sure the layout params
+ // are enclosed by a layout class (and not one that has been declared as a widget)
+ for (int i = 0 ; i < layoutParams.size();) {
+ ClassInfo layoutParamClass = layoutParams.get(i);
+ ClassInfo containingClass = layoutParamClass.containingClass();
+ if (containingClass == null || layouts.indexOf(containingClass) == -1) {
+ layoutParams.remove(i);
+ } else {
+ i++;
+ }
+ }
+
+ writeClasses(output + "/widgets.txt", widgets, layouts, layoutParams);
+ }
+
+ /**
+ * Writes a list of values into a text files.
+ * @param pathname the absolute os path of the output file.
+ * @param values the list of values to write.
+ */
+ private static void writeValues(String pathname, ArrayList<String> values) {
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try {
+ fw = new FileWriter(pathname, false);
+ bw = new BufferedWriter(fw);
+
+ for (String value : values) {
+ bw.append(value).append('\n');
+ }
+ } catch (IOException e) {
+ // pass for now
+ } finally {
+ try {
+ if (bw != null) bw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ try {
+ if (fw != null) fw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ }
+ }
+
+ /**
+ * Writes the widget/layout/layout param classes into a text files.
+ * @param pathname the absolute os path of the output file.
+ * @param widgets the list of widget classes to write.
+ * @param layouts the list of layout classes to write.
+ * @param layoutParams the list of layout param classes to write.
+ */
+ private static void writeClasses(String pathname, ArrayList<ClassInfo> widgets,
+ ArrayList<ClassInfo> layouts, ArrayList<ClassInfo> layoutParams) {
+ FileWriter fw = null;
+ BufferedWriter bw = null;
+ try {
+ fw = new FileWriter(pathname, false);
+ bw = new BufferedWriter(fw);
+
+ // write the 3 types of classes.
+ for (ClassInfo clazz : widgets) {
+ writeClass(bw, clazz, 'W');
+ }
+ for (ClassInfo clazz : layoutParams) {
+ writeClass(bw, clazz, 'P');
+ }
+ for (ClassInfo clazz : layouts) {
+ writeClass(bw, clazz, 'L');
+ }
+ } catch (IOException e) {
+ // pass for now
+ } finally {
+ try {
+ if (bw != null) bw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ try {
+ if (fw != null) fw.close();
+ } catch (IOException e) {
+ // pass for now
+ }
+ }
+ }
+
+ /**
+ * Writes a class name and its super class names into a {@link BufferedWriter}.
+ * @param writer the BufferedWriter to write into
+ * @param clazz the class to write
+ * @param prefix the prefix to put at the beginning of the line.
+ * @throws IOException
+ */
+ private static void writeClass(BufferedWriter writer, ClassInfo clazz, char prefix)
+ throws IOException {
+ writer.append(prefix).append(clazz.qualifiedName());
+ ClassInfo superClass = clazz;
+ while ((superClass = superClass.superclass()) != null) {
+ writer.append(' ').append(superClass.qualifiedName());
+ }
+ writer.append('\n');
+ }
+
+ /**
+ * Checks the inheritance of {@link ClassInfo} objects. This method return
+ * <ul>
+ * <li>{@link #TYPE_LAYOUT}: if the class extends <code>android.view.ViewGroup</code></li>
+ * <li>{@link #TYPE_WIDGET}: if the class extends <code>android.view.View</code></li>
+ * <li>{@link #TYPE_LAYOUT_PARAM}: if the class extends <code>android.view.ViewGroup$LayoutParams</code></li>
+ * <li>{@link #TYPE_NONE}: in all other cases</li>
+ * </ul>
+ * @param clazz the {@link ClassInfo} to check.
+ */
+ private static int checkInheritance(ClassInfo clazz) {
+ if ("android.view.ViewGroup".equals(clazz.qualifiedName())) {
+ return TYPE_LAYOUT;
+ } else if ("android.view.View".equals(clazz.qualifiedName())) {
+ return TYPE_WIDGET;
+ } else if ("android.view.ViewGroup.LayoutParams".equals(clazz.qualifiedName())) {
+ return TYPE_LAYOUT_PARAM;
+ }
+
+ ClassInfo parent = clazz.superclass();
+ if (parent != null) {
+ return checkInheritance(parent);
+ }
+
+ return TYPE_NONE;
+ }
+}
diff --git a/tools/droiddoc/src/Errors.java b/tools/droiddoc/src/Errors.java
new file mode 100644
index 000000000..dfeac8867
--- /dev/null
+++ b/tools/droiddoc/src/Errors.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+public class Errors
+{
+ public static boolean hadError = false;
+ private static boolean warningsAreErrors = false;
+ private static TreeSet<Message> allErrors = new TreeSet<Message>();
+
+ private static class Message implements Comparable {
+ SourcePositionInfo pos;
+ int level;
+ String msg;
+
+ Message(SourcePositionInfo p, int l, String m) {
+ pos = p;
+ level = l;
+ msg = m;
+ }
+
+ public int compareTo(Object o) {
+ Message that = (Message)o;
+ int r = this.pos.compareTo(that.pos);
+ if (r != 0) return r;
+ return this.msg.compareTo(that.msg);
+ }
+
+ public String toString() {
+ String whereText = this.pos == null ? "unknown: " : this.pos.toString() + ':';
+ return whereText + this.msg;
+ }
+ }
+
+ public static void error(Error error, SourcePositionInfo where, String text) {
+ if (error.level == HIDDEN) {
+ return;
+ }
+
+ int level = (!warningsAreErrors && error.level == WARNING) ? WARNING : ERROR;
+ String which = level == WARNING ? " warning " : " error ";
+ String message = which + error.code + ": " + text;
+
+ if (where == null) {
+ where = new SourcePositionInfo("unknown", 0, 0);
+ }
+
+ allErrors.add(new Message(where, level, message));
+
+ if (error.level == ERROR || (warningsAreErrors && error.level == WARNING)) {
+ hadError = true;
+ }
+ }
+
+ public static void printErrors() {
+ for (Message m: allErrors) {
+ if (m.level == WARNING) {
+ System.err.println(m.toString());
+ }
+ }
+ for (Message m: allErrors) {
+ if (m.level == ERROR) {
+ System.err.println(m.toString());
+ }
+ }
+ }
+
+ public static int HIDDEN = 0;
+ public static int WARNING = 1;
+ public static int ERROR = 2;
+
+ public static void setWarningsAreErrors(boolean val) {
+ warningsAreErrors = val;
+ }
+
+ public static class Error {
+ public int code;
+ public int level;
+
+ public Error(int code, int level)
+ {
+ this.code = code;
+ this.level = level;
+ }
+ }
+
+ public static Error UNRESOLVED_LINK = new Error(1, WARNING);
+ public static Error BAD_INCLUDE_TAG = new Error(2, WARNING);
+ public static Error UNKNOWN_TAG = new Error(3, WARNING);
+ public static Error UNKNOWN_PARAM_TAG_NAME = new Error(4, WARNING);
+ public static Error UNDOCUMENTED_PARAMETER = new Error(5, HIDDEN);
+ public static Error BAD_ATTR_TAG = new Error(6, ERROR);
+ public static Error BAD_INHERITDOC = new Error(7, HIDDEN);
+ public static Error HIDDEN_LINK = new Error(8, WARNING);
+ public static Error HIDDEN_CONSTRUCTOR = new Error(9, WARNING);
+ public static Error UNAVAILABLE_SYMBOL = new Error(10, ERROR);
+ public static Error HIDDEN_SUPERCLASS = new Error(11, WARNING);
+ public static Error DEPRECATED = new Error(12, HIDDEN);
+ public static Error DEPRECATION_MISMATCH = new Error(13, WARNING);
+ public static Error MISSING_COMMENT = new Error(14, WARNING);
+ public static Error IO_ERROR = new Error(15, HIDDEN);
+
+ public static Error[] ERRORS = {
+ UNRESOLVED_LINK,
+ BAD_INCLUDE_TAG,
+ UNKNOWN_TAG,
+ UNKNOWN_PARAM_TAG_NAME,
+ UNDOCUMENTED_PARAMETER,
+ BAD_ATTR_TAG,
+ BAD_INHERITDOC,
+ HIDDEN_LINK,
+ HIDDEN_CONSTRUCTOR,
+ UNAVAILABLE_SYMBOL,
+ HIDDEN_SUPERCLASS,
+ DEPRECATED,
+ IO_ERROR,
+ };
+
+ public static boolean setErrorLevel(int code, int level) {
+ for (Error e: ERRORS) {
+ if (e.code == code) {
+ e.level = level;
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/tools/droiddoc/src/FieldInfo.java b/tools/droiddoc/src/FieldInfo.java
new file mode 100644
index 000000000..536d79848
--- /dev/null
+++ b/tools/droiddoc/src/FieldInfo.java
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+import java.util.Comparator;
+
+public class FieldInfo extends MemberInfo
+{
+ public static final Comparator<FieldInfo> comparator = new Comparator<FieldInfo>() {
+ public int compare(FieldInfo a, FieldInfo b) {
+ return a.name().compareTo(b.name());
+ }
+ };
+
+ public FieldInfo(String name, ClassInfo containingClass, ClassInfo realContainingClass,
+ boolean isPublic, boolean isProtected,
+ boolean isPackagePrivate, boolean isPrivate,
+ boolean isFinal, boolean isStatic, boolean isTransient, boolean isVolatile,
+ boolean isSynthetic, TypeInfo type, String rawCommentText,
+ Object constantValue,
+ SourcePositionInfo position,
+ AnnotationInstanceInfo[] annotations)
+ {
+ super(rawCommentText, name, null, containingClass, realContainingClass,
+ isPublic, isProtected, isPackagePrivate, isPrivate,
+ isFinal, isStatic, isSynthetic, chooseKind(isFinal, isStatic), position,
+ annotations);
+ mIsTransient = isTransient;
+ mIsVolatile = isVolatile;
+ mType = type;
+ mConstantValue = constantValue;
+ }
+
+ public FieldInfo cloneForClass(ClassInfo newContainingClass) {
+ return new FieldInfo(name(), newContainingClass, realContainingClass(),
+ isPublic(), isProtected(), isPackagePrivate(),
+ isPrivate(), isFinal(), isStatic(), isTransient(), isVolatile(),
+ isSynthetic(), mType, getRawCommentText(), mConstantValue, position(),
+ annotations());
+ }
+
+ static String chooseKind(boolean isFinal, boolean isStatic)
+ {
+ if (isStatic && isFinal) {
+ return "constant";
+ } else {
+ return "field";
+ }
+ }
+
+ public TypeInfo type()
+ {
+ return mType;
+ }
+
+ public boolean isConstant()
+ {
+ return isStatic() && isFinal();
+ }
+
+ public TagInfo[] firstSentenceTags()
+ {
+ return comment().briefTags();
+ }
+
+ public TagInfo[] inlineTags()
+ {
+ return comment().tags();
+ }
+
+ public Object constantValue()
+ {
+ return mConstantValue;
+ }
+
+ public String constantLiteralValue()
+ {
+ return constantLiteralValue(mConstantValue);
+ }
+
+ public boolean isDeprecated() {
+ boolean deprecated = false;
+ if (!mDeprecatedKnown) {
+ boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+ boolean annotationDeprecated = false;
+ for (AnnotationInstanceInfo annotation : annotations()) {
+ if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+ annotationDeprecated = true;
+ break;
+ }
+ }
+
+ if (commentDeprecated != annotationDeprecated) {
+ Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+ "Field " + mContainingClass.qualifiedName() + "." + name()
+ + ": @Deprecated annotation and @deprecated comment do not match");
+ }
+
+ mIsDeprecated = commentDeprecated | annotationDeprecated;
+ mDeprecatedKnown = true;
+ }
+ return mIsDeprecated;
+ }
+
+ public static String constantLiteralValue(Object val)
+ {
+ String str = null;
+ if (val != null) {
+ if (val instanceof Boolean
+ || val instanceof Byte
+ || val instanceof Short
+ || val instanceof Integer)
+ {
+ str = val.toString();
+ }
+ //catch all special values
+ else if (val instanceof Double){
+ Double dbl = (Double) val;
+ if (dbl.toString().equals("Infinity")){
+ str = "(1.0 / 0.0)";
+ } else if (dbl.toString().equals("-Infinity")) {
+ str = "(-1.0 / 0.0)";
+ } else if (dbl.isNaN()) {
+ str = "(0.0 / 0.0)";
+ } else {
+ str = dbl.toString();
+ }
+ }
+ else if (val instanceof Long) {
+ str = val.toString() + "L";
+ }
+ else if (val instanceof Float) {
+ Float fl = (Float) val;
+ if (fl.toString().equals("Infinity")) {
+ str = "(1.0f / 0.0f)";
+ } else if (fl.toString().equals("-Infinity")) {
+ str = "(-1.0f / 0.0f)";
+ } else if (fl.isNaN()) {
+ str = "(0.0f / 0.0f)";
+ } else {
+ str = val.toString() + "f";
+ }
+ }
+ else if (val instanceof Character) {
+ str = String.format("\'\\u%04x\'", val);
+ }
+ else if (val instanceof String) {
+ str = "\"" + javaEscapeString((String)val) + "\"";
+ }
+ else {
+ str = "<<<<" +val.toString() + ">>>>";
+ }
+ }
+ if (str == null) {
+ str = "null";
+ }
+ return str;
+ }
+
+ public static String javaEscapeString(String str) {
+ String result = "";
+ final int N = str.length();
+ for (int i=0; i<N; i++) {
+ char c = str.charAt(i);
+ if (c == '\\') {
+ result += "\\\\";
+ }
+ else if (c == '\t') {
+ result += "\\t";
+ }
+ else if (c == '\b') {
+ result += "\\b";
+ }
+ else if (c == '\r') {
+ result += "\\r";
+ }
+ else if (c == '\n') {
+ result += "\\n";
+ }
+ else if (c == '\f') {
+ result += "\\f";
+ }
+ else if (c == '\'') {
+ result += "\\'";
+ }
+ else if (c == '\"') {
+ result += "\\\"";
+ }
+ else if (c >= ' ' && c <= '~') {
+ result += c;
+ }
+ else {
+ result += String.format("\\u%04x", new Integer((int)c));
+ }
+ }
+ return result;
+ }
+
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".kind", kind());
+ type().makeHDF(data, base + ".type");
+ data.setValue(base + ".name", name());
+ data.setValue(base + ".href", htmlPage());
+ data.setValue(base + ".anchor", anchor());
+ TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
+ TagInfo.makeHDF(data, base + ".descr", inlineTags());
+ TagInfo.makeHDF(data, base + ".deprecated", comment().deprecatedTags());
+ TagInfo.makeHDF(data, base + ".seeAlso", comment().seeTags());
+ data.setValue(base + ".final", isFinal() ? "final" : "");
+ data.setValue(base + ".static", isStatic() ? "static" : "");
+ if (isPublic()) {
+ data.setValue(base + ".scope", "public");
+ }
+ else if (isProtected()) {
+ data.setValue(base + ".scope", "protected");
+ }
+ else if (isPackagePrivate()) {
+ data.setValue(base + ".scope", "");
+ }
+ else if (isPrivate()) {
+ data.setValue(base + ".scope", "private");
+ }
+ Object val = mConstantValue;
+ if (val != null) {
+ String dec = null;
+ String hex = null;
+ String str = null;
+
+ if (val instanceof Boolean) {
+ str = ((Boolean)val).toString();
+ }
+ else if (val instanceof Byte) {
+ dec = String.format("%d", val);
+ hex = String.format("0x%02x", val);
+ }
+ else if (val instanceof Character) {
+ dec = String.format("\'%c\'", val);
+ hex = String.format("0x%04x", val);
+ }
+ else if (val instanceof Double) {
+ str = ((Double)val).toString();
+ }
+ else if (val instanceof Float) {
+ str = ((Float)val).toString();
+ }
+ else if (val instanceof Integer) {
+ dec = String.format("%d", val);
+ hex = String.format("0x%08x", val);
+ }
+ else if (val instanceof Long) {
+ dec = String.format("%d", val);
+ hex = String.format("0x%016x", val);
+ }
+ else if (val instanceof Short) {
+ dec = String.format("%d", val);
+ hex = String.format("0x%04x", val);
+ }
+ else if (val instanceof String) {
+ str = "\"" + ((String)val) + "\"";
+ }
+ else {
+ str = "";
+ }
+
+ if (dec != null && hex != null) {
+ data.setValue(base + ".constantValue.dec", DroidDoc.escape(dec));
+ data.setValue(base + ".constantValue.hex", DroidDoc.escape(hex));
+ }
+ else {
+ data.setValue(base + ".constantValue.str", DroidDoc.escape(str));
+ data.setValue(base + ".constantValue.isString", "1");
+ }
+ }
+ }
+
+ public boolean isExecutable()
+ {
+ return false;
+ }
+
+ public boolean isTransient()
+ {
+ return mIsTransient;
+ }
+
+ public boolean isVolatile()
+ {
+ return mIsVolatile;
+ }
+
+ boolean mIsTransient;
+ boolean mIsVolatile;
+ boolean mDeprecatedKnown;
+ boolean mIsDeprecated;
+ TypeInfo mType;
+ Object mConstantValue;
+}
+
diff --git a/tools/droiddoc/src/Hierarchy.java b/tools/droiddoc/src/Hierarchy.java
new file mode 100755
index 000000000..ac5e1dc5c
--- /dev/null
+++ b/tools/droiddoc/src/Hierarchy.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.HashMap;
+import java.util.TreeSet;
+import java.util.Set;
+import org.clearsilver.HDF;
+
+public class Hierarchy
+{
+ public static void makeHierarchy(HDF hdf, ClassInfo[] classes)
+ {
+ HashMap<String,TreeSet<String>> nodes
+ = new HashMap<String,TreeSet<String>>();
+
+ for (ClassInfo cl: classes) {
+ String name = cl.qualifiedName();
+
+ TreeSet<String> me = nodes.get(name);
+ if (me == null) {
+ me = new TreeSet<String>();
+ nodes.put(name, me);
+ }
+
+ ClassInfo superclass = cl.superclass();
+ String sname = superclass != null
+ ? superclass.qualifiedName() : null;
+ if (sname != null) {
+ TreeSet<String> s = nodes.get(sname);
+ if (s == null) {
+ s = new TreeSet<String>();
+ nodes.put(sname, s);
+ }
+ s.add(name);
+ }
+ }
+
+ /*
+ Set<String> keys = nodes.keySet();
+ for (String n: keys) {
+ System.out.println("class: " + n);
+
+ TreeSet<String> values = nodes.get(n);
+ for (String v: values) {
+ System.out.println(" - " + v);
+ }
+ }
+ */
+
+ int depth = depth(nodes, "java.lang.Object");
+
+ hdf.setValue("classes.0", "");
+ hdf.setValue("colspan", "" + depth);
+
+ recurse(nodes, "java.lang.Object", hdf.getObj("classes.0"),depth,depth);
+
+ if (false) {
+ Set<String> keys = nodes.keySet();
+ if (keys.size() > 0) {
+ System.err.println("The following classes are hidden but"
+ + " are superclasses of not-hidden classes");
+ for (String n: keys) {
+ System.err.println(" " + n);
+ }
+ }
+ }
+ }
+
+ private static int depth(HashMap<String,TreeSet<String>> nodes,
+ String name)
+ {
+ int d = 0;
+ TreeSet<String> derived = nodes.get(name);
+ if (derived != null && derived.size() > 0) {
+ for (String s: derived) {
+ int n = depth(nodes, s);
+ if (n > d) {
+ d = n;
+ }
+ }
+ }
+ return d + 1;
+ }
+
+ private static boolean exists(ClassInfo cl)
+ {
+ return cl != null && !cl.isHidden() && cl.isIncluded();
+ }
+
+ private static void recurse(HashMap<String,TreeSet<String>> nodes,
+ String name, HDF hdf,
+ int totalDepth, int remainingDepth)
+ {
+ int i;
+
+ hdf.setValue("indent", "" + (totalDepth-remainingDepth-1));
+ hdf.setValue("colspan", "" + remainingDepth);
+
+ ClassInfo cl = Converter.obtainClass(name);
+
+ hdf.setValue("class.label", cl.name());
+ hdf.setValue("class.qualified", cl.qualifiedName());
+ if (cl.checkLevel()) {
+ hdf.setValue("class.link", cl.htmlPage());
+ }
+
+ if (exists(cl)) {
+ hdf.setValue("exists", "1");
+ }
+
+ i = 0;
+ for (ClassInfo iface: cl.interfaces()) {
+ hdf.setValue("interfaces." + i + ".class.label", iface.name());
+ hdf.setValue("interfaces." + i + ".class.qualified", iface.qualifiedName());
+ if (iface.checkLevel()) {
+ hdf.setValue("interfaces." + i + ".class.link", iface.htmlPage());
+ }
+ if (exists(cl)) {
+ hdf.setValue("interfaces." + i + ".exists", "1");
+ }
+ i++;
+ }
+
+ TreeSet<String> derived = nodes.get(name);
+ if (derived != null && derived.size() > 0) {
+ hdf.setValue("derived", "");
+ HDF children = hdf.getObj("derived");
+ i = 0;
+ remainingDepth--;
+ for (String s: derived) {
+ String index = "" + i;
+ children.setValue(index, "");
+ recurse(nodes, s, children.getObj(index), totalDepth,
+ remainingDepth);
+ i++;
+ }
+ }
+
+ nodes.remove(name);
+ }
+}
+
diff --git a/tools/droiddoc/src/InheritedTags.java b/tools/droiddoc/src/InheritedTags.java
new file mode 100644
index 000000000..242170cd1
--- /dev/null
+++ b/tools/droiddoc/src/InheritedTags.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public interface InheritedTags
+{
+ TagInfo[] tags();
+ InheritedTags inherited();
+}
+
diff --git a/tools/droiddoc/src/KeywordEntry.java b/tools/droiddoc/src/KeywordEntry.java
new file mode 100644
index 000000000..7e5e357b6
--- /dev/null
+++ b/tools/droiddoc/src/KeywordEntry.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+class KeywordEntry implements Comparable
+{
+ KeywordEntry(String label, String href, String comment)
+ {
+ this.label = label;
+ this.href = href;
+ this.comment = comment;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".label", this.label);
+ data.setValue(base + ".href", this.href);
+ data.setValue(base + ".comment", this.comment);
+ }
+
+ public char firstChar()
+ {
+ return Character.toUpperCase(this.label.charAt(0));
+ }
+
+ public int compareTo(Object that)
+ {
+ return this.label.compareToIgnoreCase(((KeywordEntry)that).label);
+ }
+
+ private String label;
+ private String href;
+ private String comment;
+}
+
+
diff --git a/tools/droiddoc/src/LinkReference.java b/tools/droiddoc/src/LinkReference.java
new file mode 100644
index 000000000..bbcd4db3f
--- /dev/null
+++ b/tools/droiddoc/src/LinkReference.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import java.util.ArrayList;
+
+/**
+ * Class that represents what you see in an link or see tag. This is
+ * factored out of SeeTagInfo so it can be used elsewhere (like AttrTagInfo).
+ */
+public class LinkReference {
+
+ /** The original text. */
+ public String text;
+
+ /** The kind of this tag, if we have a new suggestion after parsing. */
+ public String kind;
+
+ /** The user visible text. */
+ public String label;
+
+ /** The link. */
+ public String href;
+
+ /** The {@link PackageInfo} if any. */
+ public PackageInfo packageInfo;
+
+ /** The {@link ClassInfo} if any. */
+ public ClassInfo classInfo;
+
+ /** The {@link MemberInfo} if any. */
+ public MemberInfo memberInfo;
+
+ /** The name of the referenced member PackageInfo} if any. */
+ public String referencedMemberName;
+
+ /** Set to true if everything is a-ok */
+ public boolean good;
+
+ /**
+ * regex pattern to use when matching explicit "<a href" reference text
+ */
+ private static final Pattern HREF_PATTERN
+ = Pattern.compile("^<a href=\"([^\"]*)\">([^<]*)</a>[ \n\r\t]*$",
+ Pattern.CASE_INSENSITIVE);
+
+ /**
+ * Parse and resolve a link string.
+ *
+ * @param text the original text
+ * @param base the class or whatever that this link is on
+ * @param pos the original position in the source document
+ * @return a new link reference. It always returns something. If there was an
+ * error, it logs it and fills in href and label with error text.
+ */
+ public static LinkReference parse(String text, ContainerInfo base, SourcePositionInfo pos,
+ boolean printOnErrors) {
+ LinkReference result = new LinkReference();
+ result.text = text;
+
+ int index;
+ int len = text.length();
+ int pairs = 0;
+ int pound = -1;
+ // split the string
+ done: {
+ for (index=0; index<len; index++) {
+ char c = text.charAt(index);
+ switch (c)
+ {
+ case '(':
+ pairs++;
+ break;
+ case '[':
+ pairs++;
+ break;
+ case ')':
+ pairs--;
+ break;
+ case ']':
+ pairs--;
+ break;
+ case ' ':
+ case '\t':
+ case '\r':
+ case '\n':
+ if (pairs == 0) {
+ break done;
+ }
+ break;
+ case '#':
+ if (pound < 0) {
+ pound = index;
+ }
+ break;
+ }
+ }
+ }
+ if (index == len && pairs != 0) {
+ Errors.error(Errors.UNRESOLVED_LINK, pos,
+ "unable to parse link/see tag: " + text.trim());
+ return result;
+ }
+
+ int linkend = index;
+
+ for (; index<len; index++) {
+ char c = text.charAt(index);
+ if (!(c == ' ' || c == '\t' || c == '\r' || c == '\n')) {
+ break;
+ }
+ }
+
+ result.label = text.substring(index);
+
+ String ref;
+ String mem;
+ if (pound == 0) {
+ ref = null;
+ mem = text.substring(1, linkend);
+ }
+ else if (pound > 0) {
+ ref = text.substring(0, pound);
+ mem = text.substring(pound+1, linkend);
+ }
+ else {
+ ref = text.substring(0, linkend);
+ mem = null;
+ }
+
+ // parse parameters, if any
+ String[] params = null;
+ String[] paramDimensions = null;
+ if (mem != null) {
+ index = mem.indexOf('(');
+ if (index > 0) {
+ ArrayList<String> paramList = new ArrayList<String>();
+ ArrayList<String> paramDimensionList = new ArrayList<String>();
+ len = mem.length();
+ int start = index+1;
+ final int START = 0;
+ final int TYPE = 1;
+ final int NAME = 2;
+ int dimension = 0;
+ int arraypair = 0;
+ int state = START;
+ int typestart = 0;
+ int typeend = -1;
+ for (int i=start; i<len; i++) {
+ char c = mem.charAt(i);
+ switch (state)
+ {
+ case START:
+ if (c!=' ' && c!='\t' && c!='\r' && c!='\n') {
+ state = TYPE;
+ typestart = i;
+ }
+ break;
+ case TYPE:
+ if (c == '[') {
+ if (typeend < 0) {
+ typeend = i;
+ }
+ dimension++;
+ arraypair++;
+ }
+ else if (c == ']') {
+ arraypair--;
+ }
+ else if (c==' ' || c=='\t' || c=='\r' || c=='\n') {
+ if (typeend < 0) {
+ typeend = i;
+ }
+ }
+ else {
+ if (typeend >= 0 || c == ')' || c == ',') {
+ if (typeend < 0) {
+ typeend = i;
+ }
+ String s = mem.substring(typestart, typeend);
+ paramList.add(s);
+ s = "";
+ for (int j=0; j<dimension; j++) {
+ s += "[]";
+ }
+ paramDimensionList.add(s);
+ state = START;
+ typeend = -1;
+ dimension = 0;
+ if (c == ',' || c == ')') {
+ state = START;
+ } else {
+ state = NAME;
+ }
+ }
+ }
+ break;
+ case NAME:
+ if (c == ',' || c == ')') {
+ state = START;
+ }
+ break;
+ }
+
+ }
+ params = paramList.toArray(new String[paramList.size()]);
+ paramDimensions = paramDimensionList.toArray(new String[paramList.size()]);
+ mem = mem.substring(0, index);
+ }
+ }
+
+ ClassInfo cl = null;
+ if (base instanceof ClassInfo) {
+ cl = (ClassInfo)base;
+ }
+
+ if (ref == null) {
+ // no class or package was provided, assume it's this class
+ if (cl != null) {
+ result.classInfo = cl;
+ }
+ } else {
+ // they provided something, maybe it's a class or a package
+ if (cl != null) {
+ result.classInfo = cl.extendedFindClass(ref);
+ if (result.classInfo == null) {
+ result.classInfo = cl.findClass(ref);
+ }
+ if (result.classInfo == null) {
+ result.classInfo = cl.findInnerClass(ref);
+ }
+ }
+ if (result.classInfo == null) {
+ result.classInfo = Converter.obtainClass(ref);
+ }
+ if (result.classInfo == null) {
+ result.packageInfo = Converter.obtainPackage(ref);
+ }
+ }
+
+ if (result.classInfo != null && mem != null) {
+ // it's either a field or a method, prefer a field
+ if (params == null) {
+ FieldInfo field = result.classInfo.findField(mem);
+ // findField looks in containing classes, so it might actually
+ // be somewhere else; link to where it really is, not what they
+ // typed.
+ if (field != null) {
+ result.classInfo = field.containingClass();
+ result.memberInfo = field;
+ }
+ }
+ if (result.memberInfo == null) {
+ MethodInfo method = result.classInfo.findMethod(mem, params, paramDimensions);
+ if (method != null) {
+ result.classInfo = method.containingClass();
+ result.memberInfo = method;
+ }
+ }
+ }
+
+ result.referencedMemberName = mem;
+ if (params != null) {
+ result.referencedMemberName = result.referencedMemberName + '(';
+ len = params.length;
+ if (len > 0) {
+ len--;
+ for (int i=0; i<len; i++) {
+ result.referencedMemberName = result.referencedMemberName + params[i]
+ + paramDimensions[i] + ", ";
+ }
+ result.referencedMemberName = result.referencedMemberName + params[len]
+ + paramDimensions[len];
+ }
+ result.referencedMemberName = result.referencedMemberName + ")";
+ }
+
+ // debugging spew
+ if (false) {
+ result.label = result.label + "/" + ref + "/" + mem + '/';
+ if (params != null) {
+ for (int i=0; i<params.length; i++) {
+ result.label += params[i] + "|";
+ }
+ }
+
+ FieldInfo f = (result.memberInfo instanceof FieldInfo)
+ ? (FieldInfo)result.memberInfo
+ : null;
+ MethodInfo m = (result.memberInfo instanceof MethodInfo)
+ ? (MethodInfo)result.memberInfo
+ : null;
+ result.label = result.label
+ + "/package=" + (result.packageInfo!=null?result.packageInfo.name():"")
+ + "/class=" + (result.classInfo!=null?result.classInfo.qualifiedName():"")
+ + "/field=" + (f!=null?f.name():"")
+ + "/method=" + (m!=null?m.name():"");
+
+ }
+
+ MethodInfo method = null;
+ boolean skipHref = false;
+
+ if (result.memberInfo != null && result.memberInfo.isExecutable()) {
+ method = (MethodInfo)result.memberInfo;
+ }
+
+ if (text.startsWith("\"")) {
+ // literal quoted reference (e.g., a book title)
+ result.label = text.substring(1);
+ skipHref = true;
+ if (!result.label.endsWith("\"")) {
+ Errors.error(Errors.UNRESOLVED_LINK, pos,
+ "unbalanced quoted link/see tag: " + text.trim());
+ result.makeError();
+ return result;
+ }
+ result.label = result.label.substring(0, result.label.length() - 1);
+ result.kind = "@seeJustLabel";
+ }
+ else if (text.startsWith("<")) {
+ // explicit "<a href" form
+ Matcher matcher = HREF_PATTERN.matcher(text);
+ if (! matcher.matches()) {
+ Errors.error(Errors.UNRESOLVED_LINK, pos,
+ "invalid <a> link/see tag: " + text.trim());
+ result.makeError();
+ return result;
+ }
+ result.href = matcher.group(1);
+ result.label = matcher.group(2);
+ result.kind = "@seeHref";
+ }
+ else if (result.packageInfo != null) {
+ result.href = result.packageInfo.htmlPage();
+ if (result.label.length() == 0) {
+ result.href = result.packageInfo.htmlPage();
+ result.label = result.packageInfo.name();
+ }
+ }
+ else if (result.classInfo != null && result.referencedMemberName == null) {
+ // class reference
+ if (result.label.length() == 0) {
+ result.label = result.classInfo.name();
+ }
+ result.href = result.classInfo.htmlPage();
+ }
+ else if (result.memberInfo != null) {
+ // member reference
+ ClassInfo containing = result.memberInfo.containingClass();
+ if (result.memberInfo.isExecutable()) {
+ if (result.referencedMemberName.indexOf('(') < 0) {
+ result.referencedMemberName += method.flatSignature();
+ }
+ }
+ if (result.label.length() == 0) {
+ result.label = result.referencedMemberName;
+ }
+ result.href = containing.htmlPage() + '#' + result.memberInfo.anchor();
+ }
+
+ if (result.href == null && !skipHref) {
+ if (printOnErrors && (base == null || base.checkLevel())) {
+ Errors.error(Errors.UNRESOLVED_LINK, pos,
+ "Unresolved link/see tag \"" + text.trim()
+ + "\" in " + ((base != null) ? base.qualifiedName() : "[null]"));
+ }
+ result.makeError();
+ }
+ else if (result.memberInfo != null && !result.memberInfo.checkLevel()) {
+ if (printOnErrors && (base == null || base.checkLevel())) {
+ Errors.error(Errors.HIDDEN_LINK, pos,
+ "Link to hidden member: " + text.trim());
+ result.href = null;
+ }
+ result.kind = "@seeJustLabel";
+ }
+ else if (result.classInfo != null && !result.classInfo.checkLevel()) {
+ if (printOnErrors && (base == null || base.checkLevel())) {
+ Errors.error(Errors.HIDDEN_LINK, pos,
+ "Link to hidden class: " + text.trim() + " label=" + result.label);
+ result.href = null;
+ }
+ result.kind = "@seeJustLabel";
+ }
+ else if (result.packageInfo != null && !result.packageInfo.checkLevel()) {
+ if (printOnErrors && (base == null || base.checkLevel())) {
+ Errors.error(Errors.HIDDEN_LINK, pos,
+ "Link to hidden package: " + text.trim());
+ result.href = null;
+ }
+ result.kind = "@seeJustLabel";
+ }
+
+ result.good = true;
+
+ return result;
+ }
+
+ public boolean checkLevel() {
+ if (memberInfo != null) {
+ return memberInfo.checkLevel();
+ }
+ if (classInfo != null) {
+ return classInfo.checkLevel();
+ }
+ if (packageInfo != null) {
+ return packageInfo.checkLevel();
+ }
+ return false;
+ }
+
+ /** turn this LinkReference into one with an error message */
+ private void makeError() {
+ //this.href = "ERROR(" + this.text.trim() + ")";
+ this.href = null;
+ if (this.label == null) {
+ this.label = "";
+ }
+ this.label = "ERROR(" + this.label + "/" + text.trim() + ")";
+ }
+
+ /** private. **/
+ private LinkReference() {
+ }
+}
diff --git a/tools/droiddoc/src/LiteralTagInfo.java b/tools/droiddoc/src/LiteralTagInfo.java
new file mode 100644
index 000000000..b39490d8a
--- /dev/null
+++ b/tools/droiddoc/src/LiteralTagInfo.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class LiteralTagInfo extends TagInfo
+{
+ private static String encode(String t)
+ {
+ t = t.replace("&", "&amp;");
+ t = t.replace("<", "&lt;");
+ t = t.replace(">", "&gt;");
+ return t;
+ }
+
+ public LiteralTagInfo(String n, String k, String t, SourcePositionInfo sp)
+ {
+ super("Text", "Text", encode(t), sp);
+ }
+}
diff --git a/tools/droiddoc/src/MemberInfo.java b/tools/droiddoc/src/MemberInfo.java
new file mode 100644
index 000000000..2a2572a46
--- /dev/null
+++ b/tools/droiddoc/src/MemberInfo.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public abstract class MemberInfo extends DocInfo implements Comparable, Scoped
+{
+ public MemberInfo(String rawCommentText, String name, String signature,
+ ClassInfo containingClass, ClassInfo realContainingClass,
+ boolean isPublic, boolean isProtected,
+ boolean isPackagePrivate, boolean isPrivate,
+ boolean isFinal, boolean isStatic, boolean isSynthetic,
+ String kind,
+ SourcePositionInfo position,
+ AnnotationInstanceInfo[] annotations)
+ {
+ super(rawCommentText, position);
+ mName = name;
+ mSignature = signature;
+ mContainingClass = containingClass;
+ mRealContainingClass = realContainingClass;
+ mIsPublic = isPublic;
+ mIsProtected = isProtected;
+ mIsPackagePrivate = isPackagePrivate;
+ mIsPrivate = isPrivate;
+ mIsFinal = isFinal;
+ mIsStatic = isStatic;
+ mIsSynthetic = isSynthetic;
+ mKind = kind;
+ mAnnotations = annotations;
+ }
+
+ public abstract boolean isExecutable();
+
+ public String anchor()
+ {
+ if (mSignature != null) {
+ return mName + mSignature;
+ } else {
+ return mName;
+ }
+ }
+
+ public String htmlPage() {
+ return mContainingClass.htmlPage() + "#" + anchor();
+ }
+
+ public int compareTo(Object that) {
+ return this.htmlPage().compareTo(((MemberInfo)that).htmlPage());
+ }
+
+ public String name()
+ {
+ return mName;
+ }
+
+ public String signature()
+ {
+ return mSignature;
+ }
+
+ public ClassInfo realContainingClass()
+ {
+ return mRealContainingClass;
+ }
+
+ public ClassInfo containingClass()
+ {
+ return mContainingClass;
+ }
+
+ public boolean isPublic()
+ {
+ return mIsPublic;
+ }
+
+ public boolean isProtected()
+ {
+ return mIsProtected;
+ }
+
+ public boolean isPackagePrivate()
+ {
+ return mIsPackagePrivate;
+ }
+
+ public boolean isPrivate()
+ {
+ return mIsPrivate;
+ }
+
+ public boolean isStatic()
+ {
+ return mIsStatic;
+ }
+
+ public boolean isFinal()
+ {
+ return mIsFinal;
+ }
+
+ public boolean isSynthetic()
+ {
+ return mIsSynthetic;
+ }
+
+ public ContainerInfo parent()
+ {
+ return mContainingClass;
+ }
+
+ public boolean checkLevel()
+ {
+ return DroidDoc.checkLevel(mIsPublic, mIsProtected,
+ mIsPackagePrivate, mIsPrivate, isHidden());
+ }
+
+ public String kind()
+ {
+ return mKind;
+ }
+
+ public AnnotationInstanceInfo[] annotations()
+ {
+ return mAnnotations;
+ }
+
+ ClassInfo mContainingClass;
+ ClassInfo mRealContainingClass;
+ String mName;
+ String mSignature;
+ boolean mIsPublic;
+ boolean mIsProtected;
+ boolean mIsPackagePrivate;
+ boolean mIsPrivate;
+ boolean mIsFinal;
+ boolean mIsStatic;
+ boolean mIsSynthetic;
+ String mKind;
+ private AnnotationInstanceInfo[] mAnnotations;
+
+}
+
diff --git a/tools/droiddoc/src/MethodInfo.java b/tools/droiddoc/src/MethodInfo.java
new file mode 100644
index 000000000..ca306653b
--- /dev/null
+++ b/tools/droiddoc/src/MethodInfo.java
@@ -0,0 +1,645 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class MethodInfo extends MemberInfo
+{
+ public static final Comparator<MethodInfo> comparator = new Comparator<MethodInfo>() {
+ public int compare(MethodInfo a, MethodInfo b) {
+ return a.name().compareTo(b.name());
+ }
+ };
+
+ private class InlineTags implements InheritedTags
+ {
+ public TagInfo[] tags()
+ {
+ return comment().tags();
+ }
+ public InheritedTags inherited()
+ {
+ MethodInfo m = findOverriddenMethod(name(), signature());
+ if (m != null) {
+ return m.inlineTags();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private static void addInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
+ {
+ for (ClassInfo i: ifaces) {
+ queue.add(i);
+ }
+ for (ClassInfo i: ifaces) {
+ addInterfaces(i.interfaces(), queue);
+ }
+ }
+
+ // first looks for a superclass, and then does a breadth first search to
+ // find the least far away match
+ public MethodInfo findOverriddenMethod(String name, String signature)
+ {
+ if (mReturnType == null) {
+ // ctor
+ return null;
+ }
+ if (mOverriddenMethod != null) {
+ return mOverriddenMethod;
+ }
+
+ ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+ addInterfaces(containingClass().interfaces(), queue);
+ for (ClassInfo iface: queue) {
+ for (MethodInfo me: iface.methods()) {
+ if (me.name().equals(name)
+ && me.signature().equals(signature)
+ && me.inlineTags().tags() != null
+ && me.inlineTags().tags().length > 0) {
+ return me;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static void addRealInterfaces(ClassInfo[] ifaces, ArrayList<ClassInfo> queue)
+ {
+ for (ClassInfo i: ifaces) {
+ queue.add(i);
+ if (i.realSuperclass() != null && i.realSuperclass().isAbstract()) {
+ queue.add(i.superclass());
+ }
+ }
+ for (ClassInfo i: ifaces) {
+ addInterfaces(i.realInterfaces(), queue);
+ }
+ }
+
+ public MethodInfo findRealOverriddenMethod(String name, String signature, HashSet notStrippable) {
+ if (mReturnType == null) {
+ // ctor
+ return null;
+ }
+ if (mOverriddenMethod != null) {
+ return mOverriddenMethod;
+ }
+
+ ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+ if (containingClass().realSuperclass() != null &&
+ containingClass().realSuperclass().isAbstract()) {
+ queue.add(containingClass());
+ }
+ addInterfaces(containingClass().realInterfaces(), queue);
+ for (ClassInfo iface: queue) {
+ for (MethodInfo me: iface.methods()) {
+ if (me.name().equals(name)
+ && me.signature().equals(signature)
+ && me.inlineTags().tags() != null
+ && me.inlineTags().tags().length > 0
+ && notStrippable.contains(me.containingClass())) {
+ return me;
+ }
+ }
+ }
+ return null;
+ }
+
+ public MethodInfo findSuperclassImplementation(HashSet notStrippable) {
+ if (mReturnType == null) {
+ // ctor
+ return null;
+ }
+ if (mOverriddenMethod != null) {
+ // Even if we're told outright that this was the overridden method, we want to
+ // be conservative and ignore mismatches of parameter types -- they arise from
+ // extending generic specializations, and we want to consider the derived-class
+ // method to be a non-override.
+ if (this.signature().equals(mOverriddenMethod.signature())) {
+ return mOverriddenMethod;
+ }
+ }
+
+ ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+ if (containingClass().realSuperclass() != null &&
+ containingClass().realSuperclass().isAbstract()) {
+ queue.add(containingClass());
+ }
+ addInterfaces(containingClass().realInterfaces(), queue);
+ for (ClassInfo iface: queue) {
+ for (MethodInfo me: iface.methods()) {
+ if (me.name().equals(this.name())
+ && me.signature().equals(this.signature())
+ && notStrippable.contains(me.containingClass())) {
+ return me;
+ }
+ }
+ }
+ return null;
+ }
+
+ public ClassInfo findRealOverriddenClass(String name, String signature) {
+ if (mReturnType == null) {
+ // ctor
+ return null;
+ }
+ if (mOverriddenMethod != null) {
+ return mOverriddenMethod.mRealContainingClass;
+ }
+
+ ArrayList<ClassInfo> queue = new ArrayList<ClassInfo>();
+ if (containingClass().realSuperclass() != null &&
+ containingClass().realSuperclass().isAbstract()) {
+ queue.add(containingClass());
+ }
+ addInterfaces(containingClass().realInterfaces(), queue);
+ for (ClassInfo iface: queue) {
+ for (MethodInfo me: iface.methods()) {
+ if (me.name().equals(name)
+ && me.signature().equals(signature)
+ && me.inlineTags().tags() != null
+ && me.inlineTags().tags().length > 0) {
+ return iface;
+ }
+ }
+ }
+ return null;
+ }
+
+ private class FirstSentenceTags implements InheritedTags
+ {
+ public TagInfo[] tags()
+ {
+ return comment().briefTags();
+ }
+ public InheritedTags inherited()
+ {
+ MethodInfo m = findOverriddenMethod(name(), signature());
+ if (m != null) {
+ return m.firstSentenceTags();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private class ReturnTags implements InheritedTags {
+ public TagInfo[] tags() {
+ return comment().returnTags();
+ }
+ public InheritedTags inherited() {
+ MethodInfo m = findOverriddenMethod(name(), signature());
+ if (m != null) {
+ return m.returnTags();
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public boolean isDeprecated() {
+ boolean deprecated = false;
+ if (!mDeprecatedKnown) {
+ boolean commentDeprecated = (comment().deprecatedTags().length > 0);
+ boolean annotationDeprecated = false;
+ for (AnnotationInstanceInfo annotation : annotations()) {
+ if (annotation.type().qualifiedName().equals("java.lang.Deprecated")) {
+ annotationDeprecated = true;
+ break;
+ }
+ }
+
+ if (commentDeprecated != annotationDeprecated) {
+ Errors.error(Errors.DEPRECATION_MISMATCH, position(),
+ "Method " + mContainingClass.qualifiedName() + "." + name()
+ + ": @Deprecated annotation and @deprecated doc tag do not match");
+ }
+
+ mIsDeprecated = commentDeprecated | annotationDeprecated;
+ mDeprecatedKnown = true;
+ }
+ return mIsDeprecated;
+ }
+
+ public TypeInfo[] getTypeParameters(){
+ return mTypeParameters;
+ }
+
+ public MethodInfo cloneForClass(ClassInfo newContainingClass) {
+ MethodInfo result = new MethodInfo(getRawCommentText(), mTypeParameters,
+ name(), signature(), newContainingClass, realContainingClass(),
+ isPublic(), isProtected(), isPackagePrivate(), isPrivate(), isFinal(), isStatic(),
+ isSynthetic(), mIsAbstract, mIsSynchronized, mIsNative, mIsAnnotationElement,
+ kind(), mFlatSignature, mOverriddenMethod,
+ mReturnType, mParameters, mThrownExceptions, position(), annotations());
+ result.init(mDefaultAnnotationElementValue);
+ return result;
+ }
+
+ public MethodInfo(String rawCommentText, TypeInfo[] typeParameters, String name,
+ String signature, ClassInfo containingClass, ClassInfo realContainingClass,
+ boolean isPublic, boolean isProtected,
+ boolean isPackagePrivate, boolean isPrivate,
+ boolean isFinal, boolean isStatic, boolean isSynthetic,
+ boolean isAbstract, boolean isSynchronized, boolean isNative,
+ boolean isAnnotationElement, String kind,
+ String flatSignature, MethodInfo overriddenMethod,
+ TypeInfo returnType, ParameterInfo[] parameters,
+ ClassInfo[] thrownExceptions, SourcePositionInfo position,
+ AnnotationInstanceInfo[] annotations)
+ {
+ // Explicitly coerce 'final' state of Java6-compiled enum values() method, to match
+ // the Java5-emitted base API description.
+ super(rawCommentText, name, signature, containingClass, realContainingClass,
+ isPublic, isProtected, isPackagePrivate, isPrivate,
+ ((name.equals("values") && containingClass.isEnum()) ? true : isFinal),
+ isStatic, isSynthetic, kind, position, annotations);
+
+ // The underlying MethodDoc for an interface's declared methods winds up being marked
+ // non-abstract. Correct that here by looking at the immediate-parent class, and marking
+ // this method abstract if it is an unimplemented interface method.
+ if (containingClass.isInterface()) {
+ isAbstract = true;
+ }
+
+ mReasonOpened = "0:0";
+ mIsAnnotationElement = isAnnotationElement;
+ mTypeParameters = typeParameters;
+ mIsAbstract = isAbstract;
+ mIsSynchronized = isSynchronized;
+ mIsNative = isNative;
+ mFlatSignature = flatSignature;
+ mOverriddenMethod = overriddenMethod;
+ mReturnType = returnType;
+ mParameters = parameters;
+ mThrownExceptions = thrownExceptions;
+ }
+
+ public void init(AnnotationValueInfo defaultAnnotationElementValue)
+ {
+ mDefaultAnnotationElementValue = defaultAnnotationElementValue;
+ }
+
+ public boolean isAbstract()
+ {
+ return mIsAbstract;
+ }
+
+ public boolean isSynchronized()
+ {
+ return mIsSynchronized;
+ }
+
+ public boolean isNative()
+ {
+ return mIsNative;
+ }
+
+ public String flatSignature()
+ {
+ return mFlatSignature;
+ }
+
+ public InheritedTags inlineTags()
+ {
+ return new InlineTags();
+ }
+
+ public InheritedTags firstSentenceTags()
+ {
+ return new FirstSentenceTags();
+ }
+
+ public InheritedTags returnTags() {
+ return new ReturnTags();
+ }
+
+ public TypeInfo returnType()
+ {
+ return mReturnType;
+ }
+
+ public String prettySignature()
+ {
+ String s = "(";
+ int N = mParameters.length;
+ for (int i=0; i<N; i++) {
+ ParameterInfo p = mParameters[i];
+ TypeInfo t = p.type();
+ if (t.isPrimitive()) {
+ s += t.simpleTypeName();
+ } else {
+ s += t.asClassInfo().name();
+ }
+ if (i != N-1) {
+ s += ',';
+ }
+ }
+ s += ')';
+ return s;
+ }
+
+ private boolean inList(ClassInfo item, ThrowsTagInfo[] list)
+ {
+ int len = list.length;
+ String qn = item.qualifiedName();
+ for (int i=0; i<len; i++) {
+ ClassInfo ex = list[i].exception();
+ if (ex != null && ex.qualifiedName().equals(qn)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ThrowsTagInfo[] throwsTags()
+ {
+ if (mThrowsTags == null) {
+ ThrowsTagInfo[] documented = comment().throwsTags();
+ ArrayList<ThrowsTagInfo> rv = new ArrayList<ThrowsTagInfo>();
+
+ int len = documented.length;
+ for (int i=0; i<len; i++) {
+ rv.add(documented[i]);
+ }
+
+ ClassInfo[] all = mThrownExceptions;
+ len = all.length;
+ for (int i=0; i<len; i++) {
+ ClassInfo cl = all[i];
+ if (documented == null || !inList(cl, documented)) {
+ rv.add(new ThrowsTagInfo("@throws", "@throws",
+ cl.qualifiedName(), cl, "",
+ containingClass(), position()));
+ }
+ }
+ mThrowsTags = rv.toArray(new ThrowsTagInfo[rv.size()]);
+ }
+ return mThrowsTags;
+ }
+
+ private static int indexOfParam(String name, String[] list)
+ {
+ final int N = list.length;
+ for (int i=0; i<N; i++) {
+ if (name.equals(list[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public ParamTagInfo[] paramTags()
+ {
+ if (mParamTags == null) {
+ final int N = mParameters.length;
+
+ String[] names = new String[N];
+ String[] comments = new String[N];
+ SourcePositionInfo[] positions = new SourcePositionInfo[N];
+
+ // get the right names so we can handle our names being different from
+ // our parent's names.
+ for (int i=0; i<N; i++) {
+ names[i] = mParameters[i].name();
+ comments[i] = "";
+ positions[i] = mParameters[i].position();
+ }
+
+ // gather our comments, and complain about misnamed @param tags
+ for (ParamTagInfo tag: comment().paramTags()) {
+ int index = indexOfParam(tag.parameterName(), names);
+ if (index >= 0) {
+ comments[index] = tag.parameterComment();
+ positions[index] = tag.position();
+ } else {
+ Errors.error(Errors.UNKNOWN_PARAM_TAG_NAME, tag.position(),
+ "@param tag with name that doesn't match the parameter list: '"
+ + tag.parameterName() + "'");
+ }
+ }
+
+ // get our parent's tags to fill in the blanks
+ MethodInfo overridden = this.findOverriddenMethod(name(), signature());
+ if (overridden != null) {
+ ParamTagInfo[] maternal = overridden.paramTags();
+ for (int i=0; i<N; i++) {
+ if (comments[i].equals("")) {
+ comments[i] = maternal[i].parameterComment();
+ positions[i] = maternal[i].position();
+ }
+ }
+ }
+
+ // construct the results, and cache them for next time
+ mParamTags = new ParamTagInfo[N];
+ for (int i=0; i<N; i++) {
+ mParamTags[i] = new ParamTagInfo("@param", "@param", names[i] + " " + comments[i],
+ parent(), positions[i]);
+
+ // while we're here, if we find any parameters that are still undocumented at this
+ // point, complain. (this warning is off by default, because it's really, really
+ // common; but, it's good to be able to enforce it)
+ if (comments[i].equals("")) {
+ Errors.error(Errors.UNDOCUMENTED_PARAMETER, positions[i],
+ "Undocumented parameter '" + names[i] + "' on method '"
+ + name() + "'");
+ }
+ }
+ }
+ return mParamTags;
+ }
+
+ public SeeTagInfo[] seeTags()
+ {
+ SeeTagInfo[] result = comment().seeTags();
+ if (result == null) {
+ if (mOverriddenMethod != null) {
+ result = mOverriddenMethod.seeTags();
+ }
+ }
+ return result;
+ }
+
+ public TagInfo[] deprecatedTags()
+ {
+ TagInfo[] result = comment().deprecatedTags();
+ if (result.length == 0) {
+ if (comment().undeprecateTags().length == 0) {
+ if (mOverriddenMethod != null) {
+ result = mOverriddenMethod.deprecatedTags();
+ }
+ }
+ }
+ return result;
+ }
+
+ public ParameterInfo[] parameters()
+ {
+ return mParameters;
+ }
+
+
+ public boolean matchesParams(String[] params, String[] dimensions)
+ {
+ if (mParamStrings == null) {
+ ParameterInfo[] mine = mParameters;
+ int len = mine.length;
+ if (len != params.length) {
+ return false;
+ }
+ for (int i=0; i<len; i++) {
+ TypeInfo t = mine[i].type();
+ if (!t.dimension().equals(dimensions[i])) {
+ return false;
+ }
+ String qn = t.qualifiedTypeName();
+ String s = params[i];
+ int slen = s.length();
+ int qnlen = qn.length();
+ if (!(qn.equals(s) ||
+ ((slen+1)<qnlen && qn.charAt(qnlen-slen-1)=='.'
+ && qn.endsWith(s)))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".kind", kind());
+ data.setValue(base + ".name", name());
+ data.setValue(base + ".href", htmlPage());
+ data.setValue(base + ".anchor", anchor());
+
+ if (mReturnType != null) {
+ returnType().makeHDF(data, base + ".returnType", false, typeVariables());
+ data.setValue(base + ".abstract", mIsAbstract ? "abstract" : "");
+ }
+
+ data.setValue(base + ".synchronized", mIsSynchronized ? "synchronized" : "");
+ data.setValue(base + ".final", isFinal() ? "final" : "");
+ data.setValue(base + ".static", isStatic() ? "static" : "");
+
+ TagInfo.makeHDF(data, base + ".shortDescr", firstSentenceTags());
+ TagInfo.makeHDF(data, base + ".descr", inlineTags());
+ TagInfo.makeHDF(data, base + ".deprecated", deprecatedTags());
+ TagInfo.makeHDF(data, base + ".seeAlso", seeTags());
+ ParamTagInfo.makeHDF(data, base + ".paramTags", paramTags());
+ AttrTagInfo.makeReferenceHDF(data, base + ".attrRefs", comment().attrTags());
+ ThrowsTagInfo.makeHDF(data, base + ".throws", throwsTags());
+ ParameterInfo.makeHDF(data, base + ".params", parameters(), isVarArgs(), typeVariables());
+ if (isProtected()) {
+ data.setValue(base + ".scope", "protected");
+ }
+ else if (isPublic()) {
+ data.setValue(base + ".scope", "public");
+ }
+ TagInfo.makeHDF(data, base + ".returns", returnTags());
+
+ if (mTypeParameters != null) {
+ TypeInfo.makeHDF(data, base + ".generic.typeArguments", mTypeParameters, false);
+ }
+ }
+
+ public HashSet<String> typeVariables()
+ {
+ HashSet<String> result = TypeInfo.typeVariables(mTypeParameters);
+ ClassInfo cl = containingClass();
+ while (cl != null) {
+ TypeInfo[] types = cl.asTypeInfo().typeArguments();
+ if (types != null) {
+ TypeInfo.typeVariables(types, result);
+ }
+ cl = cl.containingClass();
+ }
+ return result;
+ }
+
+ public boolean isExecutable()
+ {
+ return true;
+ }
+
+ public ClassInfo[] thrownExceptions()
+ {
+ return mThrownExceptions;
+ }
+
+ public String typeArgumentsName(HashSet<String> typeVars)
+ {
+ if (mTypeParameters == null || mTypeParameters.length == 0) {
+ return "";
+ } else {
+ return TypeInfo.typeArgumentsName(mTypeParameters, typeVars);
+ }
+ }
+
+ public boolean isAnnotationElement()
+ {
+ return mIsAnnotationElement;
+ }
+
+ public AnnotationValueInfo defaultAnnotationElementValue()
+ {
+ return mDefaultAnnotationElementValue;
+ }
+
+ public void setVarargs(boolean set){
+ mIsVarargs = set;
+ }
+ public boolean isVarArgs(){
+ return mIsVarargs;
+ }
+ public String toString(){
+ return this.name();
+ }
+
+ public void setReason(String reason) {
+ mReasonOpened = reason;
+ }
+
+ public String getReason() {
+ return mReasonOpened;
+ }
+
+ private String mFlatSignature;
+ private MethodInfo mOverriddenMethod;
+ private TypeInfo mReturnType;
+ private boolean mIsAnnotationElement;
+ private boolean mIsAbstract;
+ private boolean mIsSynchronized;
+ private boolean mIsNative;
+ private boolean mIsVarargs;
+ private boolean mDeprecatedKnown;
+ private boolean mIsDeprecated;
+ private ParameterInfo[] mParameters;
+ private ClassInfo[] mThrownExceptions;
+ private String[] mParamStrings;
+ ThrowsTagInfo[] mThrowsTags;
+ private ParamTagInfo[] mParamTags;
+ private TypeInfo[] mTypeParameters;
+ private AnnotationValueInfo mDefaultAnnotationElementValue;
+ private String mReasonOpened;
+}
+
diff --git a/tools/droiddoc/src/NavTree.java b/tools/droiddoc/src/NavTree.java
new file mode 100644
index 000000000..9eef0ceac
--- /dev/null
+++ b/tools/droiddoc/src/NavTree.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+
+import java.util.ArrayList;
+
+public class NavTree {
+
+ public static void writeNavTree(String dir) {
+ ArrayList<Node> children = new ArrayList();
+ for (PackageInfo pkg: DroidDoc.choosePackages()) {
+ children.add(makePackageNode(pkg));
+ }
+ Node node = new Node("Reference", dir + "packages.html", children);
+
+ StringBuilder buf = new StringBuilder();
+ if (false) {
+ // if you want a root node
+ buf.append("[");
+ node.render(buf);
+ buf.append("]");
+ } else {
+ // if you don't want a root node
+ node.renderChildren(buf);
+ }
+
+ HDF data = DroidDoc.makeHDF();
+ data.setValue("reference_tree", buf.toString());
+ ClearPage.write(data, "navtree_data.cs", "navtree_data.js");
+ }
+
+ private static Node makePackageNode(PackageInfo pkg) {
+ ArrayList<Node> children = new ArrayList();
+
+ children.add(new Node("Description", pkg.fullDescriptionHtmlPage(), null));
+
+ addClassNodes(children, "Interfaces", pkg.interfaces());
+ addClassNodes(children, "Classes", pkg.ordinaryClasses());
+ addClassNodes(children, "Enums", pkg.enums());
+ addClassNodes(children, "Exceptions", pkg.exceptions());
+ addClassNodes(children, "Errors", pkg.errors());
+
+ return new Node(pkg.name(), pkg.htmlPage(), children);
+ }
+
+ private static void addClassNodes(ArrayList<Node> parent, String label, ClassInfo[] classes) {
+ ArrayList<Node> children = new ArrayList();
+
+ for (ClassInfo cl: classes) {
+ if (cl.checkLevel()) {
+ children.add(new Node(cl.name(), cl.htmlPage(), null));
+ }
+ }
+
+ if (children.size() > 0) {
+ parent.add(new Node(label, null, children));
+ }
+ }
+
+ private static class Node {
+ private String mLabel;
+ private String mLink;
+ ArrayList<Node> mChildren;
+
+ Node(String label, String link, ArrayList<Node> children) {
+ mLabel = label;
+ mLink = link;
+ mChildren = children;
+ }
+
+ static void renderString(StringBuilder buf, String s) {
+ if (s == null) {
+ buf.append("null");
+ } else {
+ buf.append('"');
+ final int N = s.length();
+ for (int i=0; i<N; i++) {
+ char c = s.charAt(i);
+ if (c >= ' ' && c <= '~' && c != '"' && c != '\\') {
+ buf.append(c);
+ } else {
+ buf.append("\\u");
+ for (int j=0; i<4; i++) {
+ char x = (char)(c & 0x000f);
+ if (x > 10) {
+ x = (char)(x - 10 + 'a');
+ } else {
+ x = (char)(x + '0');
+ }
+ buf.append(x);
+ c >>= 4;
+ }
+ }
+ }
+ buf.append('"');
+ }
+ }
+
+ void renderChildren(StringBuilder buf) {
+ ArrayList<Node> list = mChildren;
+ if (list == null || list.size() == 0) {
+ // We output null for no children. That way empty lists here can just
+ // be a byproduct of how we generate the lists.
+ buf.append("null");
+ } else {
+ buf.append("[ ");
+ final int N = list.size();
+ for (int i=0; i<N; i++) {
+ list.get(i).render(buf);
+ if (i != N-1) {
+ buf.append(", ");
+ }
+ }
+ buf.append(" ]\n");
+ }
+ }
+
+ void render(StringBuilder buf) {
+ buf.append("[ ");
+ renderString(buf, mLabel);
+ buf.append(", ");
+ renderString(buf, mLink);
+ buf.append(", ");
+ renderChildren(buf);
+ buf.append(" ]");
+ }
+ }
+}
diff --git a/tools/droiddoc/src/PackageInfo.java b/tools/droiddoc/src/PackageInfo.java
new file mode 100644
index 000000000..aac0defbd
--- /dev/null
+++ b/tools/droiddoc/src/PackageInfo.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import com.sun.javadoc.*;
+import com.sun.tools.doclets.*;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class PackageInfo extends DocInfo implements ContainerInfo
+{
+ public static final Comparator<PackageInfo> comparator = new Comparator<PackageInfo>() {
+ public int compare(PackageInfo a, PackageInfo b) {
+ return a.name().compareTo(b.name());
+ }
+ };
+
+ public PackageInfo(PackageDoc pkg, String name, SourcePositionInfo position)
+ {
+ super(pkg.getRawCommentText(), position);
+ mName = name;
+
+ if (pkg == null) {
+ throw new RuntimeException("pkg is null");
+ }
+ mPackage = pkg;
+ }
+
+ public String htmlPage()
+ {
+ String s = mName;
+ s = s.replace('.', '/');
+ s += "/package-summary.html";
+ s = DroidDoc.javadocDir + s;
+ return s;
+ }
+
+ public String fullDescriptionHtmlPage() {
+ String s = mName;
+ s = s.replace('.', '/');
+ s += "/package-descr.html";
+ s = DroidDoc.javadocDir + s;
+ return s;
+ }
+
+ public ContainerInfo parent()
+ {
+ return null;
+ }
+
+ public boolean isHidden()
+ {
+ return comment().isHidden();
+ }
+
+ public boolean checkLevel() {
+ // TODO should return false if all classes are hidden but the package isn't.
+ // We don't have this so I'm not doing it now.
+ return !isHidden();
+ }
+
+ public String name()
+ {
+ return mName;
+ }
+
+ public String qualifiedName()
+ {
+ return mName;
+ }
+
+ public TagInfo[] inlineTags()
+ {
+ return comment().tags();
+ }
+
+ public TagInfo[] firstSentenceTags()
+ {
+ return comment().briefTags();
+ }
+
+ public static ClassInfo[] filterHidden(ClassInfo[] classes)
+ {
+ ArrayList<ClassInfo> out = new ArrayList<ClassInfo>();
+
+ for (ClassInfo cl: classes) {
+ if (!cl.isHidden()) {
+ out.add(cl);
+ }
+ }
+
+ return out.toArray(new ClassInfo[0]);
+ }
+
+ public void makeLink(HDF data, String base)
+ {
+ if (checkLevel()) {
+ data.setValue(base + ".link", htmlPage());
+ }
+ data.setValue(base + ".name", name());
+ }
+
+ public void makeClassLinkListHDF(HDF data, String base)
+ {
+ makeLink(data, base);
+ ClassInfo.makeLinkListHDF(data, base + ".interfaces", interfaces());
+ ClassInfo.makeLinkListHDF(data, base + ".classes", ordinaryClasses());
+ ClassInfo.makeLinkListHDF(data, base + ".enums", enums());
+ ClassInfo.makeLinkListHDF(data, base + ".exceptions", exceptions());
+ ClassInfo.makeLinkListHDF(data, base + ".errors", errors());
+ }
+
+ public ClassInfo[] interfaces()
+ {
+ if (mInterfaces == null) {
+ mInterfaces = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+ mPackage.interfaces())));
+ }
+ return mInterfaces;
+ }
+
+ public ClassInfo[] ordinaryClasses()
+ {
+ if (mOrdinaryClasses == null) {
+ mOrdinaryClasses = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+ mPackage.ordinaryClasses())));
+ }
+ return mOrdinaryClasses;
+ }
+
+ public ClassInfo[] enums()
+ {
+ if (mEnums == null) {
+ mEnums = ClassInfo.sortByName(filterHidden(Converter.convertClasses(mPackage.enums())));
+ }
+ return mEnums;
+ }
+
+ public ClassInfo[] exceptions()
+ {
+ if (mExceptions == null) {
+ mExceptions = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+ mPackage.exceptions())));
+ }
+ return mExceptions;
+ }
+
+ public ClassInfo[] errors()
+ {
+ if (mErrors == null) {
+ mErrors = ClassInfo.sortByName(filterHidden(Converter.convertClasses(
+ mPackage.errors())));
+ }
+ return mErrors;
+ }
+
+ // in hashed containers, treat the name as the key
+ @Override
+ public int hashCode() {
+ return mName.hashCode();
+ }
+
+ private String mName;
+ private PackageDoc mPackage;
+ private ClassInfo[] mInterfaces;
+ private ClassInfo[] mOrdinaryClasses;
+ private ClassInfo[] mEnums;
+ private ClassInfo[] mExceptions;
+ private ClassInfo[] mErrors;
+}
+
diff --git a/tools/droiddoc/src/ParamTagInfo.java b/tools/droiddoc/src/ParamTagInfo.java
new file mode 100644
index 000000000..c21ecd5a4
--- /dev/null
+++ b/tools/droiddoc/src/ParamTagInfo.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+public class ParamTagInfo extends ParsedTagInfo
+{
+ static final Pattern PATTERN = Pattern.compile(
+ "([^ \t\r\n]+)[ \t\r\n]+(.*)",
+ Pattern.DOTALL);
+
+ private boolean mIsTypeParameter;
+ private String mParameterComment;
+ private String mParameterName;
+
+ ParamTagInfo(String name, String kind, String text, ContainerInfo base,
+ SourcePositionInfo sp)
+ {
+ super(name, kind, text, base, sp);
+
+ Matcher m = PATTERN.matcher(text);
+ if (m.matches()) {
+ mParameterName = m.group(1);
+ mParameterComment = m.group(2);
+ int len = mParameterName.length();
+ mIsTypeParameter = len > 2
+ && mParameterName.charAt(0) == '<'
+ && mParameterName.charAt(len-1) == '>';
+ } else {
+ mParameterName = text.trim();
+ mParameterComment = "";
+ mIsTypeParameter = false;
+ }
+ setCommentText(mParameterComment);
+ }
+
+ ParamTagInfo(String name, String kind, String text,
+ boolean isTypeParameter, String parameterComment,
+ String parameterName, ContainerInfo base,
+ SourcePositionInfo sp)
+ {
+ super(name, kind, text, base, sp);
+ mIsTypeParameter = isTypeParameter;
+ mParameterComment = parameterComment;
+ mParameterName = parameterName;
+ }
+
+ public boolean isTypeParameter()
+ {
+ return mIsTypeParameter;
+ }
+
+ public String parameterComment()
+ {
+ return mParameterComment;
+ }
+
+ public String parameterName()
+ {
+ return mParameterName;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".name", parameterName());
+ data.setValue(base + ".isTypeParameter", isTypeParameter() ? "1" : "0");
+ TagInfo.makeHDF(data, base + ".comment", commentTags());
+ }
+
+ public static void makeHDF(HDF data, String base, ParamTagInfo[] tags)
+ {
+ for (int i=0; i<tags.length; i++) {
+ // don't output if the comment is ""
+ if (!"".equals(tags[i].parameterComment())) {
+ tags[i].makeHDF(data, base + "." + i);
+ }
+ }
+ }
+}
diff --git a/tools/droiddoc/src/ParameterInfo.java b/tools/droiddoc/src/ParameterInfo.java
new file mode 100644
index 000000000..44608bed5
--- /dev/null
+++ b/tools/droiddoc/src/ParameterInfo.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.HashSet;
+
+public class ParameterInfo
+{
+ ParameterInfo(String name, String typeName, TypeInfo type, SourcePositionInfo position)
+ {
+ mName = name;
+ mTypeName = typeName;
+ mType = type;
+ mPosition = position;
+ }
+
+ TypeInfo type()
+ {
+ return mType;
+ }
+
+ String name()
+ {
+ return mName;
+ }
+
+ String typeName()
+ {
+ return mTypeName;
+ }
+
+ SourcePositionInfo position()
+ {
+ return mPosition;
+ }
+
+ public void makeHDF(HDF data, String base, boolean isLastVararg,
+ HashSet<String> typeVariables)
+ {
+ data.setValue(base + ".name", this.name());
+ type().makeHDF(data, base + ".type", isLastVararg, typeVariables);
+ }
+
+ public static void makeHDF(HDF data, String base, ParameterInfo[] params,
+ boolean isVararg, HashSet<String> typeVariables)
+ {
+ for (int i=0; i<params.length; i++) {
+ params[i].makeHDF(data, base + "." + i,
+ isVararg && (i == params.length - 1), typeVariables);
+ }
+ }
+
+ String mName;
+ String mTypeName;
+ TypeInfo mType;
+ SourcePositionInfo mPosition;
+}
+
diff --git a/tools/droiddoc/src/ParsedTagInfo.java b/tools/droiddoc/src/ParsedTagInfo.java
new file mode 100755
index 000000000..c2e4806c1
--- /dev/null
+++ b/tools/droiddoc/src/ParsedTagInfo.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.ArrayList;
+
+public class ParsedTagInfo extends TagInfo
+{
+ private ContainerInfo mContainer;
+ private String mCommentText;
+ private Comment mComment;
+
+ ParsedTagInfo(String name, String kind, String text, ContainerInfo base, SourcePositionInfo sp)
+ {
+ super(name, kind, text, SourcePositionInfo.findBeginning(sp, text));
+ mContainer = base;
+ mCommentText = text;
+ }
+
+ public TagInfo[] commentTags()
+ {
+ if (mComment == null) {
+ mComment = new Comment(mCommentText, mContainer, position());
+ }
+ return mComment.tags();
+ }
+
+ protected void setCommentText(String comment)
+ {
+ mCommentText = comment;
+ }
+
+ public static <T extends ParsedTagInfo> TagInfo[]
+ joinTags(T[] tags)
+ {
+ ArrayList<TagInfo> list = new ArrayList<TagInfo>();
+ final int N = tags.length;
+ for (int i=0; i<N; i++) {
+ TagInfo[] t = tags[i].commentTags();
+ final int M = t.length;
+ for (int j=0; j<M; j++) {
+ list.add(t[j]);
+ }
+ }
+ return list.toArray(new TagInfo[list.size()]);
+ }
+}
diff --git a/tools/droiddoc/src/Proofread.java b/tools/droiddoc/src/Proofread.java
new file mode 100644
index 000000000..ec9f52368
--- /dev/null
+++ b/tools/droiddoc/src/Proofread.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.FileWriter;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class Proofread
+{
+ static FileWriter out = null;
+ static final Pattern WHITESPACE = Pattern.compile("\\r?\\n");
+ static final String INDENT = " ";
+ static final String NEWLINE = "\n" + INDENT;
+
+ public static void initProofread(String filename)
+ {
+ try {
+ out = new FileWriter(filename);
+ out.write("javadoc proofread file: " + filename + "\n");
+ }
+ catch (IOException e) {
+ if (out != null) {
+ try {
+ out.close();
+ }
+ catch (IOException ex) {
+ }
+ out = null;
+ }
+ System.err.println("error opening file: " + filename);
+ }
+ }
+
+ public static void finishProofread(String filename)
+ {
+ if (out == null) {
+ return;
+ }
+
+ try {
+ out.close();
+ }
+ catch (IOException e) {
+ }
+ }
+
+ public static void write(String s)
+ {
+ if (out == null) {
+ return ;
+ }
+ try {
+ out.write(s);
+ }
+ catch (IOException e) {
+ }
+ }
+
+ public static void writeIndented(String s)
+ {
+ s = s.trim();
+ Matcher m = WHITESPACE.matcher(s);
+ s = m.replaceAll(NEWLINE);
+ write(INDENT);
+ write(s);
+ write("\n");
+ }
+
+ public static void writeFileHeader(String filename)
+ {
+ write("\n\n=== ");
+ write(filename);
+ write(" ===\n");
+ }
+
+ public static void writeTagList(TagInfo[] tags)
+ {
+ if (out == null) {
+ return;
+ }
+
+ for (TagInfo t: tags) {
+ String k = t.kind();
+ if ("Text".equals(t.name())) {
+ writeIndented(t.text());
+ }
+ else if ("@more".equals(k)) {
+ writeIndented("");
+ }
+ else if ("@see".equals(k)) {
+ SeeTagInfo see = (SeeTagInfo)t;
+ String label = see.label();
+ if (label == null) {
+ label = "";
+ }
+ writeIndented("{" + see.name() + " ... " + label + "}");
+ }
+ else if ("@code".equals(k)) {
+ writeIndented(t.text());
+ }
+ else if ("@samplecode".equals(k)) {
+ writeIndented(t.text());
+ }
+ else {
+ writeIndented("{" + (t.name() != null ? t.name() : "") + "/" +
+ t.text() + "}");
+ }
+ }
+ }
+
+ public static void writePackages(String filename, TagInfo[] tags)
+ {
+ if (out == null) {
+ return;
+ }
+
+ writeFileHeader(filename);
+ writeTagList(tags);
+ }
+
+ public static void writePackage(String filename, TagInfo[] tags)
+ {
+ if (out == null) {
+ return;
+ }
+
+ writeFileHeader(filename);
+ writeTagList(tags);
+ }
+
+ public static void writeClass(String filename, ClassInfo cl)
+ {
+ if (out == null) {
+ return;
+ }
+
+ writeFileHeader(filename);
+ writeTagList(cl.inlineTags());
+
+ // enum constants
+ for (FieldInfo f: cl.enumConstants()) {
+ write("ENUM: " + f.name() + "\n");
+ writeTagList(f.inlineTags());
+ }
+
+ // fields
+ for (FieldInfo f: cl.selfFields()) {
+ write("FIELD: " + f.name() + "\n");
+ writeTagList(f.inlineTags());
+ }
+
+ // constructors
+ for (MethodInfo m: cl.constructors()) {
+ write("CONSTRUCTOR: " + m.name() + "\n");
+ writeTagList(m.inlineTags().tags());
+ }
+
+ // methods
+ for (MethodInfo m: cl.selfMethods()) {
+ write("METHOD: " + m.name() + "\n");
+ writeTagList(m.inlineTags().tags());
+ }
+ }
+}
diff --git a/tools/droiddoc/src/SampleCode.java b/tools/droiddoc/src/SampleCode.java
new file mode 100644
index 000000000..e2283bd2d
--- /dev/null
+++ b/tools/droiddoc/src/SampleCode.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+
+public class SampleCode {
+ String mSource;
+ String mDest;
+ String mTitle;
+
+ public SampleCode(String source, String dest, String title) {
+ mSource = source;
+ mTitle = title;
+ int len = dest.length();
+ if (len > 1 && dest.charAt(len-1) != '/') {
+ mDest = dest + '/';
+ } else {
+ mDest = dest;
+ }
+ }
+
+ public void write() {
+ File f = new File(mSource);
+ if (!f.isDirectory()) {
+ System.out.println("-samplecode not a directory: " + mSource);
+ return;
+ }
+ writeDirectory(f, mDest);
+ }
+
+ public static String convertExtension(String s, String ext) {
+ return s.substring(0, s.lastIndexOf('.')) + ext;
+ }
+
+ public static String[] IMAGES = { ".png", ".jpg", ".gif" };
+ public static String[] TEMPLATED = { ".java", ".xml" };
+
+ public static boolean inList(String s, String[] list) {
+ for (String t: list) {
+ if (s.endsWith(t)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void writeDirectory(File dir, String relative) {
+ TreeSet<String> dirs = new TreeSet<String>();
+ TreeSet<String> files = new TreeSet<String>();
+
+ String subdir = relative; //.substring(mDest.length());
+
+ for (File f: dir.listFiles()) {
+ String name = f.getName();
+ if (name.startsWith(".") || name.startsWith("_")) {
+ continue;
+ }
+ if (f.isFile()) {
+ String out = relative + name;
+
+ if (inList(out, IMAGES)) {
+ // copied directly
+ ClearPage.copyFile(f, out);
+ writeImagePage(f, convertExtension(out, DroidDoc.htmlExtension), subdir);
+ files.add(name);
+ }
+ if (inList(out, TEMPLATED)) {
+ // copied and goes through the template
+ ClearPage.copyFile(f, out);
+ writePage(f, convertExtension(out, DroidDoc.htmlExtension), subdir);
+ files.add(name);
+ }
+ // else ignored
+ }
+ else if (f.isDirectory()) {
+ writeDirectory(f, relative + name + "/");
+ dirs.add(name);
+ }
+ }
+
+ // write the index page
+ int i;
+ HDF hdf = DroidDoc.makeHDF();
+
+ hdf.setValue("page.title", dir.getName() + " - " + mTitle);
+ hdf.setValue("projectTitle", mTitle);
+ hdf.setValue("subdir", subdir);
+ i=0;
+ for (String d: dirs) {
+ hdf.setValue("subdirs." + i + ".name", d);
+ i++;
+ }
+ i=0;
+ for (String f: files) {
+ hdf.setValue("files." + i + ".name", f);
+ hdf.setValue("files." + i + ".href", convertExtension(f, ".html"));
+ i++;
+ }
+ String filename = dir.getPath() + "/_index.html";
+ String summary = SampleTagInfo.readFile(new SourcePositionInfo(filename, -1,-1), filename,
+ "sample code", true, false, true);
+ if (summary == null) {
+ summary = "";
+ }
+ hdf.setValue("summary", summary);
+
+ ClearPage.write(hdf, "sampleindex.cs", relative + "/index" + DroidDoc.htmlExtension);
+ }
+
+ public void writePage(File f, String out, String subdir) {
+ String name = f.getName();
+
+ String filename = f.getPath();
+ String data = SampleTagInfo.readFile(new SourcePositionInfo(filename, -1,-1), filename,
+ "sample code", true, true, true);
+ data = DroidDoc.escape(data);
+
+ HDF hdf = DroidDoc.makeHDF();
+
+ hdf.setValue("page.title", name);
+ hdf.setValue("subdir", subdir);
+ hdf.setValue("realFile", name);
+ hdf.setValue("fileContents", data);
+
+ ClearPage.write(hdf, "sample.cs", out);
+ }
+
+ public void writeImagePage(File f, String out, String subdir) {
+ String name = f.getName();
+
+ String data = "<img src=\"" + name + "\" title=\"" + name + "\" />";
+
+ HDF hdf = DroidDoc.makeHDF();
+
+ hdf.setValue("page.title", name);
+ hdf.setValue("subdir", subdir);
+ hdf.setValue("realFile", name);
+ hdf.setValue("fileContents", data);
+
+ ClearPage.write(hdf, "sample.cs", out);
+ }
+}
diff --git a/tools/droiddoc/src/SampleTagInfo.java b/tools/droiddoc/src/SampleTagInfo.java
new file mode 100644
index 000000000..c80083bdc
--- /dev/null
+++ b/tools/droiddoc/src/SampleTagInfo.java
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+import java.io.Reader;
+import java.io.IOException;
+import java.io.FileReader;
+import java.io.LineNumberReader;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+/*
+ * SampleTagInfo copies text from a given file into the javadoc comment.
+ *
+ * The @include tag copies the text verbatim from the given file.
+ *
+ * The @sample tag copies the text from the given file, stripping leading and
+ * trailing whitespace, and reducing the indent level of the text to the indent
+ * level of the first non-whitespace line.
+ *
+ * Both tags accept either a filename and an id or just a filename. If no id
+ * is provided, the entire file is copied. If an id is provided, the lines
+ * in the given file between the first two lines containing BEGIN_INCLUDE(id)
+ * and END_INCLUDE(id), for the given id, are copied. The id may be only
+ * letters, numbers and underscore (_).
+ *
+ * Four examples:
+ * {@include samples/ApiDemos/src/com/google/app/Notification1.java}
+ * {@sample samples/ApiDemos/src/com/google/app/Notification1.java}
+ * {@include samples/ApiDemos/src/com/google/app/Notification1.java Bleh}
+ * {@sample samples/ApiDemos/src/com/google/app/Notification1.java Bleh}
+ *
+ */
+public class SampleTagInfo extends TagInfo
+{
+ static final int STATE_BEGIN = 0;
+ static final int STATE_MATCHING = 1;
+
+ static final Pattern TEXT = Pattern.compile(
+ "[\r\n \t]*([^\r\n \t]*)[\r\n \t]*([0-9A-Za-z_]*)[\r\n \t]*",
+ Pattern.DOTALL);
+
+ private static final String BEGIN_INCLUDE = "BEGIN_INCLUDE";
+ private static final String END_INCLUDE = "END_INCLUDE";
+
+ private ContainerInfo mBase;
+ private String mIncluded;
+
+ public static String escapeHtml(String str) {
+ return str.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;");
+ }
+
+ private static boolean isIncludeLine(String str) {
+ return str.indexOf(BEGIN_INCLUDE)>=0 || str.indexOf(END_INCLUDE)>=0;
+ }
+
+ SampleTagInfo(String name, String kind, String text, ContainerInfo base,
+ SourcePositionInfo position)
+ {
+ super(name, kind, text, position);
+ mBase = base;
+
+ Matcher m = TEXT.matcher(text);
+ if (!m.matches()) {
+ Errors.error(Errors.BAD_INCLUDE_TAG, position, "Bad @include tag: "
+ + text);
+ return;
+ }
+ String filename = m.group(1);
+ String id = m.group(2);
+ boolean trim = "@sample".equals(name);
+
+ if (id == null || "".equals(id)) {
+ mIncluded = readFile(position, filename, id, trim, true, false);
+ } else {
+ mIncluded = loadInclude(position, filename, id, trim);
+ }
+
+ if (mIncluded == null) {
+ Errors.error(Errors.BAD_INCLUDE_TAG, position, "include tag '" + id
+ + "' not found in file: " + filename);
+ }
+ }
+
+ static String getTrimString(String line)
+ {
+ int i = 0;
+ int len = line.length();
+ for (; i<len; i++) {
+ char c = line.charAt(i);
+ if (c != ' ' && c != '\t') {
+ break;
+ }
+ }
+ if (i == len) {
+ return null;
+ } else {
+ return line.substring(0, i);
+ }
+ }
+
+ static String loadInclude(SourcePositionInfo pos, String filename,
+ String id, boolean trim)
+ {
+ Reader input = null;
+ StringBuilder result = new StringBuilder();
+
+ String begin = BEGIN_INCLUDE + "(" + id + ")";
+ String end = END_INCLUDE + "(" + id + ")";
+
+ try {
+ input = new FileReader(filename);
+ LineNumberReader lines = new LineNumberReader(input);
+
+ int state = STATE_BEGIN;
+
+ int trimLength = -1;
+ String trimString = null;
+ int trailing = 0;
+
+ while (true) {
+ String line = lines.readLine();
+ if (line == null) {
+ return null;
+ }
+ switch (state) {
+ case STATE_BEGIN:
+ if (line.indexOf(begin) >= 0) {
+ state = STATE_MATCHING;
+ }
+ break;
+ case STATE_MATCHING:
+ if (line.indexOf(end) >= 0) {
+ return result.substring(0);
+ } else {
+ boolean empty = "".equals(line.trim());
+ if (trim) {
+ if (isIncludeLine(line)) {
+ continue;
+ }
+ if (trimLength < 0 && !empty) {
+ trimString = getTrimString(line);
+ if (trimString != null) {
+ trimLength = trimString.length();
+ }
+ }
+ if (trimLength >= 0 && line.length() > trimLength) {
+ boolean trimThisLine = true;
+ for (int i=0; i<trimLength; i++) {
+ if (line.charAt(i) != trimString.charAt(i)){
+ trimThisLine = false;
+ break;
+ }
+ }
+ if (trimThisLine) {
+ line = line.substring(trimLength);
+ }
+ }
+ if (trimLength >= 0) {
+ if (!empty) {
+ for (int i=0; i<trailing; i++) {
+ result.append('\n');
+ }
+ line = escapeHtml(line);
+ result.append(line);
+ trailing = 1; // add \n next time, maybe
+ } else {
+ trailing++;
+ }
+ }
+ } else {
+ result.append(line);
+ result.append('\n');
+ }
+ }
+ break;
+ }
+ }
+ }
+ catch (IOException e) {
+ Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Error reading file for"
+ + " include \"" + id + "\" " + filename);
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException ex) {
+ }
+ }
+ }
+ Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Did not find " + end
+ + " in file " + filename);
+ return null;
+ }
+
+ static String readFile(SourcePositionInfo pos, String filename,
+ String id, boolean trim, boolean escape,
+ boolean errorOk)
+ {
+ Reader input = null;
+ StringBuilder result = new StringBuilder();
+ int trailing = 0;
+ boolean started = false;
+ try {
+ input = new FileReader(filename);
+ LineNumberReader lines = new LineNumberReader(input);
+
+ while (true) {
+ String line = lines.readLine();
+ if (line == null) {
+ break;
+ }
+ if (trim) {
+ if (isIncludeLine(line)) {
+ continue;
+ }
+ if (!"".equals(line.trim())) {
+ if (started) {
+ for (int i=0; i<trailing; i++) {
+ result.append('\n');
+ }
+ }
+ if (escape) {
+ line = escapeHtml(line);
+ }
+ result.append(line);
+ trailing = 1; // add \n next time, maybe
+ started = true;
+ } else {
+ if (started) {
+ trailing++;
+ }
+ }
+ } else {
+ result.append(line);
+ result.append('\n');
+ }
+ }
+ }
+ catch (IOException e) {
+ if (errorOk) {
+ return null;
+ } else {
+ Errors.error(Errors.BAD_INCLUDE_TAG, pos, "Error reading file for"
+ + " include \"" + id + "\" " + filename);
+ }
+ }
+ finally {
+ if (input != null) {
+ try {
+ input.close();
+ }
+ catch (IOException ex) {
+ }
+ }
+ }
+ return result.substring(0);
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".name", name());
+ data.setValue(base + ".kind", kind());
+ if (mIncluded != null) {
+ data.setValue(base + ".text", mIncluded);
+ } else {
+ data.setValue(base + ".text", "INCLUDE_ERROR");
+ }
+ }
+}
+
diff --git a/tools/droiddoc/src/Scoped.java b/tools/droiddoc/src/Scoped.java
new file mode 100644
index 000000000..cca61edb5
--- /dev/null
+++ b/tools/droiddoc/src/Scoped.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public interface Scoped {
+ boolean isPublic();
+ boolean isProtected();
+ boolean isPackagePrivate();
+ boolean isPrivate();
+ boolean isHidden();
+}
diff --git a/tools/droiddoc/src/SeeTagInfo.java b/tools/droiddoc/src/SeeTagInfo.java
new file mode 100644
index 000000000..94863b504
--- /dev/null
+++ b/tools/droiddoc/src/SeeTagInfo.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.ArrayList;
+
+public class SeeTagInfo extends TagInfo
+{
+ private ContainerInfo mBase;
+ LinkReference mLink;
+
+ SeeTagInfo(String name, String kind, String text, ContainerInfo base,
+ SourcePositionInfo position)
+ {
+ super(name, kind, text, position);
+ mBase = base;
+ }
+
+ protected LinkReference linkReference() {
+ if (mLink == null) {
+ mLink = LinkReference.parse(text(), mBase, position(),
+ (!"@see".equals(name())) && (mBase != null ? mBase.checkLevel() : true));
+ }
+ return mLink;
+ }
+
+ public String label()
+ {
+ return linkReference().label;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ LinkReference linkRef = linkReference();
+ if (linkRef.kind != null) {
+ // if they have a better suggestion about "kind" use that.
+ // do this before super.makeHDF() so it picks it up
+ setKind(linkRef.kind);
+ }
+
+ super.makeHDF(data, base);
+
+ data.setValue(base + ".label", linkRef.label);
+ if (linkRef.href != null) {
+ data.setValue(base + ".href", linkRef.href);
+ }
+ }
+
+ public boolean checkLevel() {
+ return linkReference().checkLevel();
+ }
+
+ public static void makeHDF(HDF data, String base, SeeTagInfo[] tags)
+ {
+ int j=0;
+ for (SeeTagInfo tag: tags) {
+ if (tag.mBase.checkLevel() && tag.checkLevel()) {
+ tag.makeHDF(data, base + "." + j);
+ j++;
+ }
+ }
+ }
+}
diff --git a/tools/droiddoc/src/Sorter.java b/tools/droiddoc/src/Sorter.java
new file mode 100644
index 000000000..92039d441
--- /dev/null
+++ b/tools/droiddoc/src/Sorter.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Sorter implements Comparable
+{
+ public String label;
+ public Object data;
+
+ public Sorter(String l, Object d)
+ {
+ label = l;
+ data = d;
+ }
+
+ public int compareTo(Object other)
+ {
+ return label.compareToIgnoreCase(((Sorter)other).label);
+ }
+}
diff --git a/tools/droiddoc/src/SourcePositionInfo.java b/tools/droiddoc/src/SourcePositionInfo.java
new file mode 100644
index 000000000..62448032f
--- /dev/null
+++ b/tools/droiddoc/src/SourcePositionInfo.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class SourcePositionInfo implements Comparable
+{
+ public SourcePositionInfo() {
+ this.file = "<unknown>";
+ this.line = 0;
+ this.column = 0;
+ }
+
+ public SourcePositionInfo(String file, int line, int column)
+ {
+ this.file = file;
+ this.line = line;
+ this.column = column;
+ }
+
+ public SourcePositionInfo(SourcePositionInfo that)
+ {
+ this.file = that.file;
+ this.line = that.line;
+ this.column = that.column;
+ }
+
+ /**
+ * Given this position and str which occurs at that position, as well as str an index into str,
+ * find the SourcePositionInfo.
+ *
+ * @throw StringIndexOutOfBoundsException if index &gt; str.length()
+ */
+ public static SourcePositionInfo add(SourcePositionInfo that, String str, int index)
+ {
+ if (that == null) {
+ return null;
+ }
+ int line = that.line;
+ char prev = 0;
+ for (int i=0; i<index; i++) {
+ char c = str.charAt(i);
+ if (c == '\r' || (c == '\n' && prev != '\r')) {
+ line++;
+ }
+ prev = c;
+ }
+ return new SourcePositionInfo(that.file, line, 0);
+ }
+
+ public static SourcePositionInfo findBeginning(SourcePositionInfo that, String str)
+ {
+ if (that == null) {
+ return null;
+ }
+ int line = that.line-1; // -1 because, well, it seems to work
+ int prev = 0;
+ for (int i=str.length()-1; i>=0; i--) {
+ char c = str.charAt(i);
+ if ((c == '\r' && prev != '\n') || (c == '\n')) {
+ line--;
+ }
+ prev = c;
+ }
+ return new SourcePositionInfo(that.file, line, 0);
+ }
+
+ public String toString()
+ {
+ return file + ':' + line;
+ }
+
+ public int compareTo(Object o) {
+ SourcePositionInfo that = (SourcePositionInfo)o;
+ int r = this.file.compareTo(that.file);
+ if (r != 0) return r;
+ return this.line - that.line;
+ }
+
+ public String file;
+ public int line;
+ public int column;
+}
diff --git a/tools/droiddoc/src/Stubs.java b/tools/droiddoc/src/Stubs.java
new file mode 100644
index 000000000..e1ec76a30
--- /dev/null
+++ b/tools/droiddoc/src/Stubs.java
@@ -0,0 +1,988 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.Comparator;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+
+public class Stubs {
+ private static HashSet<ClassInfo> notStrippable;
+ public static void writeStubs(String stubsDir, Boolean writeXML, String xmlFile,
+ HashSet<String> stubPackages) {
+ // figure out which classes we need
+ notStrippable = new HashSet();
+ ClassInfo[] all = Converter.allClasses();
+ File xml = new File(xmlFile);
+ xml.getParentFile().mkdirs();
+ PrintStream xmlWriter = null;
+ if (writeXML) {
+ try {
+ xmlWriter = new PrintStream(xml);
+ } catch (FileNotFoundException e) {
+ Errors.error(Errors.IO_ERROR, new SourcePositionInfo(xmlFile, 0, 0),
+ "Cannot open file for write.");
+ }
+ }
+ // If a class is public or protected, not hidden, and marked as included,
+ // then we can't strip it
+ for (ClassInfo cl: all) {
+ if (cl.checkLevel() && cl.isIncluded()) {
+ cantStripThis(cl, notStrippable, "0:0");
+ }
+ }
+
+ // complain about anything that looks includeable but is not supposed to
+ // be written, e.g. hidden things
+ for (ClassInfo cl: notStrippable) {
+ if (!cl.isHidden()) {
+ MethodInfo[] methods = cl.selfMethods();
+ for (MethodInfo m: methods) {
+ if (m.isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL,
+ m.position(), "Reference to hidden method "
+ + m.name());
+ } else if (m.isDeprecated()) {
+ // don't bother reporting deprecated methods
+ // unless they are public
+ Errors.error(Errors.DEPRECATED,
+ m.position(), "Method "
+ + cl.qualifiedName() + "." + m.name()
+ + " is deprecated");
+ }
+
+ ClassInfo returnClass = m.returnType().asClassInfo();
+ if (returnClass != null && returnClass.isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL, m.position(),
+ "Method " + cl.qualifiedName() + "." + m.name()
+ + " returns unavailable type " + returnClass.name());
+ }
+
+ ParameterInfo[] params = m.parameters();
+ for (ParameterInfo p: params) {
+ TypeInfo t = p.type();
+ if (!t.isPrimitive()) {
+ if (t.asClassInfo().isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL,
+ m.position(), "Parameter of hidden type "
+ + t.fullName() + " in "
+ + cl.qualifiedName() + "." + m.name() + "()");
+ }
+ }
+ }
+ }
+
+ // annotations are handled like methods
+ methods = cl.annotationElements();
+ for (MethodInfo m: methods) {
+ if (m.isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL,
+ m.position(), "Reference to hidden annotation "
+ + m.name());
+ }
+
+ ClassInfo returnClass = m.returnType().asClassInfo();
+ if (returnClass != null && returnClass.isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL,
+ m.position(), "Annotation '" + m.name()
+ + "' returns unavailable type " + returnClass.name());
+ }
+
+ ParameterInfo[] params = m.parameters();
+ for (ParameterInfo p: params) {
+ TypeInfo t = p.type();
+ if (!t.isPrimitive()) {
+ if (t.asClassInfo().isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL,
+ p.position(), "Reference to unavailable annotation class "
+ + t.fullName());
+ }
+ }
+ }
+ }
+ } else if (cl.isDeprecated()) {
+ // not hidden, but deprecated
+ Errors.error(Errors.DEPRECATED,
+ cl.position(), "Class " + cl.qualifiedName()
+ + " is deprecated");
+ }
+ }
+
+ // write out the stubs
+ HashMap<PackageInfo, List<ClassInfo>> packages = new HashMap<PackageInfo, List<ClassInfo>>();
+ for (ClassInfo cl: notStrippable) {
+ if (!cl.isDocOnly()) {
+ if (stubPackages == null || stubPackages.contains(cl.containingPackage().name())) {
+ writeClassFile(stubsDir, cl);
+ if (packages.containsKey(cl.containingPackage())) {
+ packages.get(cl.containingPackage()).add(cl);
+ } else {
+ ArrayList<ClassInfo> classes = new ArrayList<ClassInfo>();
+ classes.add(cl);
+ packages.put(cl.containingPackage(), classes);
+ }
+ }
+ }
+ }
+
+ // write out the XML
+ if (writeXML && xmlWriter != null) {
+ writeXML(xmlWriter, packages, notStrippable);
+ }
+
+ if (xmlWriter != null) {
+ xmlWriter.close();
+ }
+
+ }
+
+ public static void cantStripThis(ClassInfo cl, HashSet<ClassInfo> notStrippable, String why) {
+
+ if (!notStrippable.add(cl)) {
+ // slight optimization: if it already contains cl, it already contains
+ // all of cl's parents
+ return;
+ }
+ cl.setReasonIncluded(why);
+
+ // cant strip annotations
+ /*if (cl.annotations() != null){
+ for (AnnotationInstanceInfo ai : cl.annotations()){
+ if (ai.type() != null){
+ cantStripThis(ai.type(), notStrippable, "1:" + cl.qualifiedName());
+ }
+ }
+ }*/
+ // cant strip any public fields or their generics
+ if (cl.allSelfFields() != null){
+ for (FieldInfo fInfo : cl.allSelfFields()){
+ if (fInfo.type() != null){
+ if (fInfo.type().asClassInfo() != null){
+ cantStripThis(fInfo.type().asClassInfo(), notStrippable,
+ "2:" + cl.qualifiedName());
+ }
+ if (fInfo.type().typeArguments() != null){
+ for (TypeInfo tTypeInfo : fInfo.type().typeArguments()){
+ if (tTypeInfo.asClassInfo() != null){
+ cantStripThis(tTypeInfo.asClassInfo(), notStrippable,
+ "3:" + cl.qualifiedName());
+ }
+ }
+ }
+ }
+ }
+ }
+ //cant strip any of the type's generics
+ if (cl.asTypeInfo() != null){
+ if (cl.asTypeInfo().typeArguments() != null){
+ for (TypeInfo tInfo : cl.asTypeInfo().typeArguments()){
+ if (tInfo.asClassInfo() != null){
+ cantStripThis(tInfo.asClassInfo(), notStrippable, "4:" + cl.qualifiedName());
+ }
+ }
+ }
+ }
+ //cant strip any of the annotation elements
+ //cantStripThis(cl.annotationElements(), notStrippable);
+ // take care of methods
+ cantStripThis(cl.allSelfMethods(), notStrippable);
+ cantStripThis(cl.allConstructors(), notStrippable);
+ // blow the outer class open if this is an inner class
+ if(cl.containingClass() != null){
+ cantStripThis(cl.containingClass(), notStrippable, "5:" + cl.qualifiedName());
+ }
+ // blow open super class and interfaces
+ ClassInfo supr = cl.realSuperclass();
+ if (supr != null) {
+ if (supr.isHidden()) {
+ // cl is a public class declared as extending a hidden superclass.
+ // this is not a desired practice but it's happened, so we deal
+ // with it by stripping off the superclass relation for purposes of
+ // generating the doc & stub information, and proceeding normally.
+ cl.init(cl.asTypeInfo(), cl.realInterfaces(), cl.realInterfaceTypes(),
+ cl.innerClasses(), cl.allConstructors(), cl.allSelfMethods(),
+ cl.annotationElements(), cl.allSelfFields(), cl.enumConstants(),
+ cl.containingPackage(), cl.containingClass(),
+ null, null, cl.annotations());
+ Errors.error(Errors.HIDDEN_SUPERCLASS,
+ cl.position(), "Public class " + cl.qualifiedName()
+ + " stripped of unavailable superclass "
+ + supr.qualifiedName());
+ } else {
+ cantStripThis(supr, notStrippable, "6:" + cl.realSuperclass().name()
+ + cl.qualifiedName());
+ }
+ }
+ }
+
+ private static void cantStripThis(MethodInfo[] mInfos , HashSet<ClassInfo> notStrippable) {
+ //for each method, blow open the parameters, throws and return types. also blow open their generics
+ if (mInfos != null){
+ for (MethodInfo mInfo : mInfos){
+ if (mInfo.getTypeParameters() != null){
+ for (TypeInfo tInfo : mInfo.getTypeParameters()){
+ if (tInfo.asClassInfo() != null){
+ cantStripThis(tInfo.asClassInfo(), notStrippable, "8:" +
+ mInfo.realContainingClass().qualifiedName() + ":" +
+ mInfo.name());
+ }
+ }
+ }
+ if (mInfo.parameters() != null){
+ for (ParameterInfo pInfo : mInfo.parameters()){
+ if (pInfo.type() != null && pInfo.type().asClassInfo() != null){
+ cantStripThis(pInfo.type().asClassInfo(), notStrippable,
+ "9:"+ mInfo.realContainingClass().qualifiedName()
+ + ":" + mInfo.name());
+ if (pInfo.type().typeArguments() != null){
+ for (TypeInfo tInfoType : pInfo.type().typeArguments()){
+ if (tInfoType.asClassInfo() != null){
+ ClassInfo tcl = tInfoType.asClassInfo();
+ if (tcl.isHidden()) {
+ Errors.error(Errors.UNAVAILABLE_SYMBOL, mInfo.position(),
+ "Parameter of hidden type "
+ + tInfoType.fullName() + " in "
+ + mInfo.containingClass().qualifiedName()
+ + '.' + mInfo.name() + "()");
+ } else {
+ cantStripThis(tcl, notStrippable,
+ "10:" +
+ mInfo.realContainingClass().qualifiedName() + ":" +
+ mInfo.name());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ for (ClassInfo thrown : mInfo.thrownExceptions()){
+ cantStripThis(thrown, notStrippable, "11:" +
+ mInfo.realContainingClass().qualifiedName()
+ +":" + mInfo.name());
+ }
+ if (mInfo.returnType() != null && mInfo.returnType().asClassInfo() != null){
+ cantStripThis(mInfo.returnType().asClassInfo(), notStrippable,
+ "12:" + mInfo.realContainingClass().qualifiedName() +
+ ":" + mInfo.name());
+ if (mInfo.returnType().typeArguments() != null){
+ for (TypeInfo tyInfo: mInfo.returnType().typeArguments() ){
+ if (tyInfo.asClassInfo() != null){
+ cantStripThis(tyInfo.asClassInfo(), notStrippable,
+ "13:" +
+ mInfo.realContainingClass().qualifiedName()
+ + ":" + mInfo.name());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ static String javaFileName(ClassInfo cl) {
+ String dir = "";
+ PackageInfo pkg = cl.containingPackage();
+ if (pkg != null) {
+ dir = pkg.name();
+ dir = dir.replace('.', '/') + '/';
+ }
+ return dir + cl.name() + ".java";
+ }
+
+ static void writeClassFile(String stubsDir, ClassInfo cl) {
+ // inner classes are written by their containing class
+ if (cl.containingClass() != null) {
+ return;
+ }
+
+ String filename = stubsDir + '/' + javaFileName(cl);
+ File file = new File(filename);
+ ClearPage.ensureDirectory(file);
+
+ PrintStream stream = null;
+ try {
+ stream = new PrintStream(file);
+ writeClassFile(stream, cl);
+ }
+ catch (FileNotFoundException e) {
+ System.err.println("error writing file: " + filename);
+ }
+ finally {
+ if (stream != null) {
+ stream.close();
+ }
+ }
+ }
+
+ static void writeClassFile(PrintStream stream, ClassInfo cl) {
+ PackageInfo pkg = cl.containingPackage();
+ if (pkg != null) {
+ stream.println("package " + pkg.name() + ";");
+ }
+ writeClass(stream, cl);
+ }
+
+ static void writeClass(PrintStream stream, ClassInfo cl) {
+ writeAnnotations(stream, cl.annotations());
+
+ stream.print(DroidDoc.scope(cl) + " ");
+ if (cl.isAbstract() && !cl.isAnnotation() && !cl.isInterface()) {
+ stream.print("abstract ");
+ }
+ if (cl.isStatic()){
+ stream.print("static ");
+ }
+ if (cl.isFinal() && !cl.isEnum()) {
+ stream.print("final ");
+ }
+ if (false) {
+ stream.print("strictfp ");
+ }
+
+ HashSet<String> classDeclTypeVars = new HashSet();
+ String leafName = cl.asTypeInfo().fullName(classDeclTypeVars);
+ int bracket = leafName.indexOf('<');
+ if (bracket < 0) bracket = leafName.length() - 1;
+ int period = leafName.lastIndexOf('.', bracket);
+ if (period < 0) period = -1;
+ leafName = leafName.substring(period+1);
+
+ String kind = cl.kind();
+ stream.println(kind + " " + leafName);
+
+ TypeInfo base = cl.superclassType();
+
+ if (!"enum".equals(kind)) {
+ if (base != null && !"java.lang.Object".equals(base.qualifiedTypeName())) {
+ stream.println(" extends " + base.fullName(classDeclTypeVars));
+ }
+ }
+
+ TypeInfo[] interfaces = cl.realInterfaceTypes();
+ List<TypeInfo> usedInterfaces = new ArrayList<TypeInfo>();
+ for (TypeInfo iface : interfaces) {
+ if (notStrippable.contains(iface.asClassInfo())
+ && !iface.asClassInfo().isDocOnly()) {
+ usedInterfaces.add(iface);
+ }
+ }
+ if (usedInterfaces.size() > 0 && !cl.isAnnotation()) {
+ // can java annotations extend other ones?
+ if (cl.isInterface() || cl.isAnnotation()) {
+ stream.print(" extends ");
+ } else {
+ stream.print(" implements ");
+ }
+ String comma = "";
+ for (TypeInfo iface: usedInterfaces) {
+ stream.print(comma + iface.fullName(classDeclTypeVars));
+ comma = ", ";
+ }
+ stream.println();
+ }
+
+ stream.println("{");
+
+ FieldInfo[] enumConstants = cl.enumConstants();
+ int N = enumConstants.length;
+ for (int i=0; i<N; i++) {
+ FieldInfo field = enumConstants[i];
+ if (!field.constantLiteralValue().equals("null")){
+ stream.println(field.name() + "(" + field.constantLiteralValue()
+ + (i==N-1 ? ");" : "),"));
+ }else{
+ stream.println(field.name() + "(" + (i==N-1 ? ");" : "),"));
+ }
+ }
+
+ for (ClassInfo inner: cl.getRealInnerClasses()) {
+ if (notStrippable.contains(inner)
+ && !inner.isDocOnly()){
+ writeClass(stream, inner);
+ }
+ }
+
+
+ for (MethodInfo method: cl.constructors()) {
+ if (!method.isDocOnly()) {
+ writeMethod(stream, method, true);
+ }
+ }
+
+ boolean fieldNeedsInitialization = false;
+ boolean staticFieldNeedsInitialization = false;
+ for (FieldInfo field: cl.allSelfFields()) {
+ if (!field.isDocOnly()) {
+ if (!field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
+ fieldNeedsInitialization = true;
+ }
+ if (field.isStatic() && field.isFinal() && !fieldIsInitialized(field)) {
+ staticFieldNeedsInitialization = true;
+ }
+ }
+ }
+
+ // The compiler includes a default public constructor that calls the super classes
+ // default constructor in the case where there are no written constructors.
+ // So, if we hide all the constructors, java may put in a constructor
+ // that calls a nonexistent super class constructor. So, if there are no constructors,
+ // and the super class doesn't have a default constructor, write in a private constructor
+ // that works. TODO -- we generate this as protected, but we really should generate
+ // it as private unless it also exists in the real code.
+ if ((cl.constructors().length == 0 && (cl.getNonWrittenConstructors().length != 0
+ || fieldNeedsInitialization))
+ && !cl.isAnnotation()
+ && !cl.isInterface()
+ && !cl.isEnum() ) {
+ //Errors.error(Errors.HIDDEN_CONSTRUCTOR,
+ // cl.position(), "No constructors " +
+ // "found and superclass has no parameterless constructor. A constructor " +
+ // "that calls an appropriate superclass constructor " +
+ // "was automatically written to stubs.\n");
+ stream.println(cl.leafName()
+ + "() { " + superCtorCall(cl,null)
+ + "throw new" + " RuntimeException(\"Stub!\"); }");
+ }
+
+ for (MethodInfo method: cl.allSelfMethods()) {
+ if (cl.isEnum()) {
+ if (("values".equals(method.name())
+ && "()".equals(method.signature()))
+ || ("valueOf".equals(method.name())
+ && "(java.lang.String)".equals(method.signature()))) {
+ // skip these two methods on enums, because they're synthetic,
+ // although for some reason javadoc doesn't mark them as synthetic,
+ // maybe because they still want them documented
+ continue;
+ }
+ }
+ if (!method.isDocOnly()) {
+ writeMethod(stream, method, false);
+ }
+ }
+ //Write all methods that are hidden, but override abstract methods or interface methods.
+ //These can't be hidden.
+ for (MethodInfo method : cl.getHiddenMethods()){
+ MethodInfo overriddenMethod = method.findRealOverriddenMethod(method.name(), method.signature(), notStrippable);
+ ClassInfo classContainingMethod = method.findRealOverriddenClass(method.name(),
+ method.signature());
+ if (overriddenMethod != null && !overriddenMethod.isHidden()
+ && !overriddenMethod.isDocOnly() &&
+ (overriddenMethod.isAbstract() ||
+ overriddenMethod.containingClass().isInterface())) {
+ method.setReason("1:" + classContainingMethod.qualifiedName());
+ cl.addMethod(method);
+ writeMethod(stream, method, false);
+ }
+ }
+
+ for (MethodInfo element: cl.annotationElements()) {
+ if (!element.isDocOnly()) {
+ writeAnnotationElement(stream, element);
+ }
+ }
+
+ for (FieldInfo field: cl.allSelfFields()) {
+ if (!field.isDocOnly()) {
+ writeField(stream, field);
+ }
+ }
+
+ if (staticFieldNeedsInitialization) {
+ stream.print("static { ");
+ for (FieldInfo field: cl.allSelfFields()) {
+ if (!field.isDocOnly() && field.isStatic() && field.isFinal()
+ && !fieldIsInitialized(field) && field.constantValue() == null) {
+ stream.print(field.name() + " = " + field.type().defaultValue()
+ + "; ");
+ }
+ }
+ stream.println("}");
+ }
+
+ stream.println("}");
+ }
+
+
+ static void writeMethod(PrintStream stream, MethodInfo method, boolean isConstructor) {
+ String comma;
+
+ stream.print(DroidDoc.scope(method) + " ");
+ if (method.isStatic()) {
+ stream.print("static ");
+ }
+ if (method.isFinal()) {
+ stream.print("final ");
+ }
+ if (method.isAbstract()) {
+ stream.print("abstract ");
+ }
+ if (method.isSynchronized()) {
+ stream.print("synchronized ");
+ }
+ if (method.isNative()) {
+ stream.print("native ");
+ }
+ if (false /*method.isStictFP()*/) {
+ stream.print("strictfp ");
+ }
+
+ stream.print(method.typeArgumentsName(new HashSet()) + " ");
+
+ if (!isConstructor) {
+ stream.print(method.returnType().fullName(method.typeVariables()) + " ");
+ }
+ String n = method.name();
+ int pos = n.lastIndexOf('.');
+ if (pos >= 0) {
+ n = n.substring(pos + 1);
+ }
+ stream.print(n + "(");
+ comma = "";
+ int count = 1;
+ int size = method.parameters().length;
+ for (ParameterInfo param: method.parameters()) {
+ stream.print(comma + fullParameterTypeName(method, param.type(), count == size)
+ + " " + param.name());
+ comma = ", ";
+ count++;
+ }
+ stream.print(")");
+
+ comma = "";
+ if (method.thrownExceptions().length > 0) {
+ stream.print(" throws ");
+ for (ClassInfo thrown: method.thrownExceptions()) {
+ stream.print(comma + thrown.qualifiedName());
+ comma = ", ";
+ }
+ }
+ if (method.isAbstract() || method.isNative() || method.containingClass().isInterface()) {
+ stream.println(";");
+ } else {
+ stream.print(" { ");
+ if (isConstructor) {
+ stream.print(superCtorCall(method.containingClass(), method.thrownExceptions()));
+ }
+ stream.println("throw new RuntimeException(\"Stub!\"); }");
+ }
+ }
+
+ static void writeField(PrintStream stream, FieldInfo field) {
+ stream.print(DroidDoc.scope(field) + " ");
+ if (field.isStatic()) {
+ stream.print("static ");
+ }
+ if (field.isFinal()) {
+ stream.print("final ");
+ }
+ if (field.isTransient()) {
+ stream.print("transient ");
+ }
+ if (field.isVolatile()) {
+ stream.print("volatile ");
+ }
+
+ stream.print(field.type().fullName());
+ stream.print(" ");
+ stream.print(field.name());
+
+ if (fieldIsInitialized(field)) {
+ stream.print(" = " + field.constantLiteralValue());
+ }
+
+ stream.println(";");
+ }
+
+ static boolean fieldIsInitialized(FieldInfo field) {
+ return (field.isFinal() && field.constantValue() != null)
+ || !field.type().dimension().equals("")
+ || field.containingClass().isInterface();
+ }
+
+ // Returns 'true' if the method is an @Override of a visible parent
+ // method implementation, and thus does not affect the API.
+ static boolean methodIsOverride(MethodInfo mi) {
+ // Abstract/static/final methods are always listed in the API description
+ if (mi.isAbstract() || mi.isStatic() || mi.isFinal()) {
+ return false;
+ }
+
+ // Find any relevant ancestor declaration and inspect it
+ MethodInfo om = mi.findSuperclassImplementation(notStrippable);
+ if (om != null) {
+ // Visibility mismatch is an API change, so check for it
+ if (mi.mIsPrivate == om.mIsPrivate
+ && mi.mIsPublic == om.mIsPublic
+ && mi.mIsProtected == om.mIsProtected) {
+ // Look only for overrides of an ancestor class implementation,
+ // not of e.g. an abstract or interface method declaration
+ if (!om.isAbstract()) {
+ // If the parent is hidden, we can't rely on it to provide
+ // the API
+ if (!om.isHidden()) {
+ // If the only "override" turns out to be in our own class
+ // (which sometimes happens in concrete subclasses of
+ // abstract base classes), it's not really an override
+ if (!mi.mContainingClass.equals(om.mContainingClass)) {
+ return true;
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ static boolean canCallMethod(ClassInfo from, MethodInfo m) {
+ if (m.isPublic() || m.isProtected()) {
+ return true;
+ }
+ if (m.isPackagePrivate()) {
+ String fromPkg = from.containingPackage().name();
+ String pkg = m.containingClass().containingPackage().name();
+ if (fromPkg.equals(pkg)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // call a constructor, any constructor on this class's superclass.
+ static String superCtorCall(ClassInfo cl, ClassInfo[] thrownExceptions) {
+ ClassInfo base = cl.realSuperclass();
+ if (base == null) {
+ return "";
+ }
+ HashSet<String> exceptionNames = new HashSet<String>();
+ if (thrownExceptions != null ){
+ for (ClassInfo thrown : thrownExceptions){
+ exceptionNames.add(thrown.name());
+ }
+ }
+ MethodInfo[] ctors = base.constructors();
+ MethodInfo ctor = null;
+ //bad exception indicates that the exceptions thrown by the super constructor
+ //are incompatible with the constructor we're using for the sub class.
+ Boolean badException = false;
+ for (MethodInfo m: ctors) {
+ if (canCallMethod(cl, m)) {
+ if (m.thrownExceptions() != null){
+ for (ClassInfo thrown : m.thrownExceptions()){
+ if (!exceptionNames.contains(thrown.name())){
+ badException = true;
+ }
+ }
+ }
+ if (badException){
+ badException = false;
+ continue;
+ }
+ // if it has no args, we're done
+ if (m.parameters().length == 0) {
+ return "";
+ }
+ ctor = m;
+ }
+ }
+ if (ctor != null) {
+ String result = "";
+ result+= "super(";
+ ParameterInfo[] params = ctor.parameters();
+ int N = params.length;
+ for (int i=0; i<N; i++) {
+ TypeInfo t = params[i].type();
+ if (t.isPrimitive() && t.dimension().equals("")) {
+ String n = t.simpleTypeName();
+ if (("byte".equals(n)
+ || "short".equals(n)
+ || "int".equals(n)
+ || "long".equals(n)
+ || "float".equals(n)
+ || "double".equals(n)) && t.dimension().equals("")) {
+ result += "0";
+ }
+ else if ("char".equals(n)) {
+ result += "'\\0'";
+ }
+ else if ("boolean".equals(n)) {
+ result += "false";
+ }
+ else {
+ result += "<<unknown-" + n + ">>";
+ }
+ } else {
+ //put null in each super class method. Cast null to the correct type
+ //to avoid collisions with other constructors. If the type is generic
+ //don't cast it
+ result += (!t.isTypeVariable() ? "(" + t.qualifiedTypeName() + t.dimension() +
+ ")" : "") + "null";
+ }
+ if (i != N-1) {
+ result += ",";
+ }
+ }
+ result += "); ";
+ return result;
+ } else {
+ return "";
+ }
+ }
+
+ static void writeAnnotations(PrintStream stream, AnnotationInstanceInfo[] annotations) {
+ for (AnnotationInstanceInfo ann: annotations) {
+ if (!ann.type().isHidden()) {
+ stream.println(ann.toString());
+ }
+ }
+ }
+
+ static void writeAnnotationElement(PrintStream stream, MethodInfo ann) {
+ stream.print(ann.returnType().fullName());
+ stream.print(" ");
+ stream.print(ann.name());
+ stream.print("()");
+ AnnotationValueInfo def = ann.defaultAnnotationElementValue();
+ if (def != null) {
+ stream.print(" default ");
+ stream.print(def.valueString());
+ }
+ stream.println(";");
+ }
+
+ static void writeXML(PrintStream xmlWriter, HashMap<PackageInfo, List<ClassInfo>> allClasses,
+ HashSet notStrippable) {
+ // extract the set of packages, sort them by name, and write them out in that order
+ Set<PackageInfo> allClassKeys = allClasses.keySet();
+ PackageInfo[] allPackages = allClassKeys.toArray(new PackageInfo[allClassKeys.size()]);
+ Arrays.sort(allPackages, PackageInfo.comparator);
+
+ xmlWriter.println("<api>");
+ for (PackageInfo pack : allPackages) {
+ writePackageXML(xmlWriter, pack, allClasses.get(pack), notStrippable);
+ }
+ xmlWriter.println("</api>");
+ }
+
+ static void writePackageXML(PrintStream xmlWriter, PackageInfo pack, List<ClassInfo> classList,
+ HashSet notStrippable) {
+ ClassInfo[] classes = classList.toArray(new ClassInfo[classList.size()]);
+ Arrays.sort(classes, ClassInfo.comparator);
+ xmlWriter.println("<package name=\"" + pack.name() + "\"\n"
+ //+ " source=\"" + pack.position() + "\"\n"
+ + ">");
+ for (ClassInfo cl : classes) {
+ writeClassXML(xmlWriter, cl, notStrippable);
+ }
+ xmlWriter.println("</package>");
+
+
+ }
+
+ static void writeClassXML(PrintStream xmlWriter, ClassInfo cl, HashSet notStrippable) {
+ String scope = DroidDoc.scope(cl);
+ String deprecatedString = "";
+ String declString = (cl.isInterface()) ? "interface" : "class";
+ if (cl.isDeprecated()) {
+ deprecatedString = "deprecated";
+ } else {
+ deprecatedString = "not deprecated";
+ }
+ xmlWriter.println("<" + declString + " name=\"" + cl.name() + "\"");
+ if (!cl.isInterface() && !cl.qualifiedName().equals("java.lang.Object")) {
+ xmlWriter.println(" extends=\"" + ((cl.realSuperclass() == null)
+ ? "java.lang.Object"
+ : cl.realSuperclass().qualifiedName()) + "\"");
+ }
+ xmlWriter.println(" abstract=\"" + cl.isAbstract() + "\"\n"
+ + " static=\"" + cl.isStatic() + "\"\n"
+ + " final=\"" + cl.isFinal() + "\"\n"
+ + " deprecated=\"" + deprecatedString + "\"\n"
+ + " visibility=\"" + scope + "\"\n"
+ //+ " source=\"" + cl.position() + "\"\n"
+ + ">");
+
+ ClassInfo[] interfaces = cl.realInterfaces();
+ Arrays.sort(interfaces, ClassInfo.comparator);
+ for (ClassInfo iface : interfaces) {
+ if (notStrippable.contains(iface)) {
+ xmlWriter.println("<implements name=\"" + iface.qualifiedName() + "\">");
+ xmlWriter.println("</implements>");
+ }
+ }
+
+ MethodInfo[] constructors = cl.constructors();
+ Arrays.sort(constructors, MethodInfo.comparator);
+ for (MethodInfo mi : constructors) {
+ writeConstructorXML(xmlWriter, mi);
+ }
+
+ MethodInfo[] methods = cl.allSelfMethods();
+ Arrays.sort(methods, MethodInfo.comparator);
+ for (MethodInfo mi : methods) {
+ if (!methodIsOverride(mi)) {
+ writeMethodXML(xmlWriter, mi);
+ }
+ }
+
+ FieldInfo[] fields = cl.allSelfFields();
+ Arrays.sort(fields, FieldInfo.comparator);
+ for (FieldInfo fi : fields) {
+ writeFieldXML(xmlWriter, fi);
+ }
+ xmlWriter.println("</" + declString + ">");
+
+ }
+
+ static void writeMethodXML(PrintStream xmlWriter, MethodInfo mi) {
+ String scope = DroidDoc.scope(mi);
+
+ String deprecatedString = "";
+ if (mi.isDeprecated()) {
+ deprecatedString = "deprecated";
+ } else {
+ deprecatedString = "not deprecated";
+ }
+ xmlWriter.println("<method name=\"" + mi.name() + "\"\n"
+ + ((mi.returnType() != null)
+ ? " return=\"" + makeXMLcompliant(fullParameterTypeName(mi, mi.returnType(), false)) + "\"\n"
+ : "")
+ + " abstract=\"" + mi.isAbstract() + "\"\n"
+ + " native=\"" + mi.isNative() + "\"\n"
+ + " synchronized=\"" + mi.isSynchronized() + "\"\n"
+ + " static=\"" + mi.isStatic() + "\"\n"
+ + " final=\"" + mi.isFinal() + "\"\n"
+ + " deprecated=\""+ deprecatedString + "\"\n"
+ + " visibility=\"" + scope + "\"\n"
+ //+ " source=\"" + mi.position() + "\"\n"
+ + ">");
+
+ // write parameters in declaration order
+ int numParameters = mi.parameters().length;
+ int count = 0;
+ for (ParameterInfo pi : mi.parameters()) {
+ count++;
+ writeParameterXML(xmlWriter, mi, pi, count == numParameters);
+ }
+
+ // but write exceptions in canonicalized order
+ ClassInfo[] exceptions = mi.thrownExceptions();
+ Arrays.sort(exceptions, ClassInfo.comparator);
+ for (ClassInfo pi : exceptions) {
+ xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
+ + "\">");
+ xmlWriter.println("</exception>");
+ }
+ xmlWriter.println("</method>");
+ }
+
+ static void writeConstructorXML(PrintStream xmlWriter, MethodInfo mi) {
+ String scope = DroidDoc.scope(mi);
+ String deprecatedString = "";
+ if (mi.isDeprecated()) {
+ deprecatedString = "deprecated";
+ } else {
+ deprecatedString = "not deprecated";
+ }
+ xmlWriter.println("<constructor name=\"" + mi.name() + "\"\n"
+ + " type=\"" + mi.containingClass().qualifiedName() + "\"\n"
+ + " static=\"" + mi.isStatic() + "\"\n"
+ + " final=\"" + mi.isFinal() + "\"\n"
+ + " deprecated=\"" + deprecatedString + "\"\n"
+ + " visibility=\"" + scope +"\"\n"
+ //+ " source=\"" + mi.position() + "\"\n"
+ + ">");
+
+ int numParameters = mi.parameters().length;
+ int count = 0;
+ for (ParameterInfo pi : mi.parameters()) {
+ count++;
+ writeParameterXML(xmlWriter, mi, pi, count == numParameters);
+ }
+
+ ClassInfo[] exceptions = mi.thrownExceptions();
+ Arrays.sort(exceptions, ClassInfo.comparator);
+ for (ClassInfo pi : exceptions) {
+ xmlWriter.println("<exception name=\"" + pi.name() +"\" type=\"" + pi.qualifiedName()
+ + "\">");
+ xmlWriter.println("</exception>");
+ }
+ xmlWriter.println("</constructor>");
+ }
+
+ static void writeParameterXML(PrintStream xmlWriter, MethodInfo method,
+ ParameterInfo pi, boolean isLast) {
+ xmlWriter.println("<parameter name=\"" + pi.name() + "\" type=\"" +
+ makeXMLcompliant(fullParameterTypeName(method, pi.type(), isLast)) + "\">");
+ xmlWriter.println("</parameter>");
+ }
+
+ static void writeFieldXML(PrintStream xmlWriter, FieldInfo fi) {
+ String scope = DroidDoc.scope(fi);
+ String deprecatedString = "";
+ if (fi.isDeprecated()) {
+ deprecatedString = "deprecated";
+ } else {
+ deprecatedString = "not deprecated";
+ }
+ //need to make sure value is valid XML
+ String value = makeXMLcompliant(fi.constantLiteralValue());
+
+ String fullTypeName = makeXMLcompliant(fi.type().qualifiedTypeName())
+ + fi.type().dimension();
+
+ xmlWriter.println("<field name=\"" + fi.name() +"\"\n"
+ + " type=\"" + fullTypeName + "\"\n"
+ + " transient=\"" + fi.isTransient() + "\"\n"
+ + " volatile=\"" + fi.isVolatile() + "\"\n"
+ + (fieldIsInitialized(fi) ? " value=\"" + value + "\"\n" : "")
+ + " static=\"" + fi.isStatic() + "\"\n"
+ + " final=\"" + fi.isFinal() + "\"\n"
+ + " deprecated=\"" + deprecatedString + "\"\n"
+ + " visibility=\"" + scope + "\"\n"
+ //+ " source=\"" + fi.position() + "\"\n"
+ + ">");
+ xmlWriter.println("</field>");
+ }
+
+ static String makeXMLcompliant(String s) {
+ String returnString = "";
+ returnString = s.replaceAll("&", "&amp;");
+ returnString = returnString.replaceAll("<", "&lt;");
+ returnString = returnString.replaceAll(">", "&gt;");
+ returnString = returnString.replaceAll("\"", "&quot;");
+ returnString = returnString.replaceAll("'", "&pos;");
+ return returnString;
+ }
+
+ static String fullParameterTypeName(MethodInfo method, TypeInfo type, boolean isLast) {
+ String fullTypeName = type.fullName(method.typeVariables());
+ if (isLast && method.isVarArgs()) {
+ // TODO: note that this does not attempt to handle hypothetical
+ // vararg methods whose last parameter is a list of arrays, e.g.
+ // "Object[]...".
+ fullTypeName = type.fullNameNoDimension(method.typeVariables()) + "...";
+ }
+ return fullTypeName;
+ }
+}
diff --git a/tools/droiddoc/src/TagInfo.java b/tools/droiddoc/src/TagInfo.java
new file mode 100644
index 000000000..d25c50028
--- /dev/null
+++ b/tools/droiddoc/src/TagInfo.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+
+public class TagInfo
+{
+ private String mName;
+ private String mText;
+ private String mKind;
+ private SourcePositionInfo mPosition;
+
+ TagInfo(String n, String k, String t, SourcePositionInfo sp)
+ {
+ mName = n;
+ mText = t;
+ mKind = k;
+ mPosition = sp;
+ }
+
+ String name()
+ {
+ return mName;
+ }
+
+ String text()
+ {
+ return mText;
+ }
+
+ String kind()
+ {
+ return mKind;
+ }
+
+ SourcePositionInfo position() {
+ return mPosition;
+ }
+
+ void setKind(String kind) {
+ mKind = kind;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ data.setValue(base + ".name", name());
+ data.setValue(base + ".text", text());
+ data.setValue(base + ".kind", kind());
+ }
+
+ public static void makeHDF(HDF data, String base, TagInfo[] tags)
+ {
+ makeHDF(data, base, tags, null, 0, 0);
+ }
+
+ public static void makeHDF(HDF data, String base, InheritedTags tags)
+ {
+ makeHDF(data, base, tags.tags(), tags.inherited(), 0, 0);
+ }
+
+ private static int makeHDF(HDF data, String base, TagInfo[] tags,
+ InheritedTags inherited, int j, int depth)
+ {
+ int i;
+ int len = tags.length;
+ if (len == 0 && inherited != null) {
+ j = makeHDF(data, base, inherited.tags(), inherited.inherited(), j, depth+1);
+ } else {
+ for (i=0; i<len; i++, j++) {
+ TagInfo t = tags[i];
+ if (inherited != null && t.name().equals("@inheritDoc")) {
+ j = makeHDF(data, base, inherited.tags(),
+ inherited.inherited(), j, depth+1);
+ } else {
+ if (t.name().equals("@inheritDoc")) {
+ Errors.error(Errors.BAD_INHERITDOC, t.mPosition,
+ "@inheritDoc on class/method that is not inherited");
+ }
+ t.makeHDF(data, base + "." + j);
+ }
+ }
+ }
+ return j;
+ }
+}
+
diff --git a/tools/droiddoc/src/TextTagInfo.java b/tools/droiddoc/src/TextTagInfo.java
new file mode 100644
index 000000000..dcdfdd997
--- /dev/null
+++ b/tools/droiddoc/src/TextTagInfo.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class TextTagInfo extends TagInfo {
+ TextTagInfo(String n, String k, String t, SourcePositionInfo p) {
+ super(n, k, DroidDoc.escape(t), p);
+ }
+}
diff --git a/tools/droiddoc/src/ThrowsTagInfo.java b/tools/droiddoc/src/ThrowsTagInfo.java
new file mode 100644
index 000000000..318a57d2e
--- /dev/null
+++ b/tools/droiddoc/src/ThrowsTagInfo.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.regex.Pattern;
+import java.util.regex.Matcher;
+
+public class ThrowsTagInfo extends ParsedTagInfo
+{
+ static final Pattern PATTERN = Pattern.compile(
+ "(\\S+)\\s+(.*)",
+ Pattern.DOTALL);
+ private ClassInfo mException;
+
+ public ThrowsTagInfo(String name, String kind, String text,
+ ContainerInfo base, SourcePositionInfo sp)
+ {
+ super(name, kind, text, base, sp);
+
+ Matcher m = PATTERN.matcher(text);
+ if (m.matches()) {
+ setCommentText(m.group(2));
+ String className = m.group(1);
+ if (base instanceof ClassInfo) {
+ mException = ((ClassInfo)base).findClass(className);
+ }
+ if (mException == null) {
+ mException = Converter.obtainClass(className);
+ }
+ }
+ }
+
+ public ThrowsTagInfo(String name, String kind, String text,
+ ClassInfo exception, String exceptionComment,
+ ContainerInfo base, SourcePositionInfo sp)
+ {
+ super(name, kind, text, base, sp);
+ mException = exception;
+ setCommentText(exceptionComment);
+ }
+
+ public ClassInfo exception()
+ {
+ return mException;
+ }
+
+ public TypeInfo exceptionType()
+ {
+ if (mException != null) {
+ return mException.asTypeInfo();
+ } else {
+ return null;
+ }
+ }
+
+ public static void makeHDF(HDF data, String base, ThrowsTagInfo[] tags)
+ {
+ for (int i=0; i<tags.length; i++) {
+ TagInfo.makeHDF(data, base + '.' + i + ".comment",
+ tags[i].commentTags());
+ if (tags[i].exceptionType() != null) {
+ tags[i].exceptionType().makeHDF(data, base + "." + i + ".type");
+ }
+ }
+ }
+
+
+}
+
diff --git a/tools/droiddoc/src/TodoFile.java b/tools/droiddoc/src/TodoFile.java
new file mode 100644
index 000000000..ebef27e13
--- /dev/null
+++ b/tools/droiddoc/src/TodoFile.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class TodoFile {
+
+ public static final String MISSING = "No description text";
+
+ public static boolean areTagsUseful(InheritedTags tags) {
+ while (tags != null) {
+ if (areTagsUseful(tags.tags())) {
+ return true;
+ }
+ tags = tags.inherited();
+ }
+ return false;
+ }
+
+ public static boolean areTagsUseful(TagInfo[] tags) {
+ for (TagInfo t: tags) {
+ if ("Text".equals(t.name()) && t.text().trim().length() != 0) {
+ return true;
+ }
+ if ("@inheritDoc".equals(t.name())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static void setHDF(HDF data, String base, SourcePositionInfo pos, String name,
+ String descr) {
+ data.setValue(base + ".pos", pos.toString());
+ data.setValue(base + ".name", name);
+ data.setValue(base + ".descr", descr);
+ }
+
+ static class PackageStats {
+ String name;
+ public int total;
+ public int errors;
+ }
+
+ public static String percent(int a, int b) {
+ return ""+Math.round((((b-a)/(float)b))*100) + "%";
+ }
+
+ public static void writeTodoFile(String filename) {
+ HDF data = DroidDoc.makeHDF();
+ DroidDoc.setPageTitle(data, "Missing Documentation");
+ TreeMap<String,PackageStats> packageStats = new TreeMap<String,PackageStats>();
+
+ ClassInfo[] classes = Converter.rootClasses();
+ Arrays.sort(classes);
+
+ int classIndex = 0;
+
+ for (ClassInfo cl: classes) {
+ if (cl.isHidden()) {
+ continue;
+ }
+
+ String classBase = "classes." + classIndex;
+
+ String base = classBase + ".errors.";
+ int errors = 0;
+ int total = 1;
+
+ if (!areTagsUseful(cl.inlineTags())) {
+ setHDF(data, base + errors, cl.position(), "&lt;class comment&gt;", MISSING);
+ errors++;
+ }
+
+
+ for (MethodInfo m: cl.constructors()) {
+ boolean good = true;
+ total++;
+ if (m.checkLevel()) {
+ if (!areTagsUseful(m.inlineTags())) {
+ setHDF(data, base + errors, m.position(), m.name() + m.prettySignature(),
+ MISSING);
+ good = false;
+ }
+ }
+ if (!good) {
+ errors++;
+ }
+ }
+
+ for (MethodInfo m: cl.selfMethods()) {
+ boolean good = true;
+ total++;
+ if (m.checkLevel()) {
+ if (!areTagsUseful(m.inlineTags())) {
+ setHDF(data, base + errors, m.position(), m.name() + m.prettySignature(),
+ MISSING);
+ good = false;
+ }
+ }
+ if (!good) {
+ errors++;
+ }
+ }
+
+
+ for (FieldInfo f: cl.enumConstants()) {
+ boolean good = true;
+ total++;
+ if (f.checkLevel()) {
+ if (!areTagsUseful(f.inlineTags())) {
+ setHDF(data, base + errors, f.position(), f.name(), MISSING);
+ good = false;
+ }
+ }
+ if (!good) {
+ errors++;
+ }
+ }
+
+ for (FieldInfo f: cl.selfFields()) {
+ boolean good = true;
+ total++;
+ if (f.checkLevel()) {
+ if (!areTagsUseful(f.inlineTags())) {
+ setHDF(data, base + errors, f.position(), f.name(), MISSING);
+ good = false;
+ }
+ }
+ if (!good) {
+ errors++;
+ }
+ }
+
+ if (errors > 0) {
+ data.setValue(classBase + ".qualified", cl.qualifiedName());
+ data.setValue(classBase + ".errorCount", ""+errors);
+ data.setValue(classBase + ".totalCount", ""+total);
+ data.setValue(classBase + ".percentGood", percent(errors, total));
+ }
+
+ PackageInfo pkg = cl.containingPackage();
+ String pkgName = pkg != null ? pkg.name() : "";
+ PackageStats ps = packageStats.get(pkgName);
+ if (ps == null) {
+ ps = new PackageStats();
+ ps.name = pkgName;
+ packageStats.put(pkgName, ps);
+ }
+ ps.total += total;
+ ps.errors += errors;
+
+ classIndex++;
+ }
+
+ int allTotal = 0;
+ int allErrors = 0;
+
+ int i = 0;
+ for (PackageStats ps: packageStats.values()) {
+ data.setValue("packages." + i + ".name", ""+ps.name);
+ data.setValue("packages." + i + ".errorCount", ""+ps.errors);
+ data.setValue("packages." + i + ".totalCount", ""+ps.total);
+ data.setValue("packages." + i + ".percentGood", percent(ps.errors, ps.total));
+
+ allTotal += ps.total;
+ allErrors += ps.errors;
+
+ i++;
+ }
+
+ data.setValue("all.errorCount", ""+allErrors);
+ data.setValue("all.totalCount", ""+allTotal);
+ data.setValue("all.percentGood", percent(allErrors, allTotal));
+
+ ClearPage.write(data, "todo.cs", filename, true);
+ }
+}
+
diff --git a/tools/droiddoc/src/TypeInfo.java b/tools/droiddoc/src/TypeInfo.java
new file mode 100644
index 000000000..c1119de5e
--- /dev/null
+++ b/tools/droiddoc/src/TypeInfo.java
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.clearsilver.HDF;
+import org.clearsilver.CS;
+import java.util.*;
+import java.io.*;
+
+public class TypeInfo
+{
+ public TypeInfo(boolean isPrimitive, String dimension,
+ String simpleTypeName, String qualifiedTypeName,
+ ClassInfo cl)
+ {
+ mIsPrimitive = isPrimitive;
+ mDimension = dimension;
+ mSimpleTypeName = simpleTypeName;
+ mQualifiedTypeName = qualifiedTypeName;
+ mClass = cl;
+ }
+
+ public ClassInfo asClassInfo()
+ {
+ return mClass;
+ }
+
+ public boolean isPrimitive()
+ {
+ return mIsPrimitive;
+ }
+
+ public String dimension()
+ {
+ return mDimension;
+ }
+
+ public String simpleTypeName()
+ {
+ return mSimpleTypeName;
+ }
+
+ public String qualifiedTypeName()
+ {
+ return mQualifiedTypeName;
+ }
+
+ public String fullName()
+ {
+ if (mFullName != null) {
+ return mFullName;
+ } else {
+ return fullName(new HashSet());
+ }
+ }
+
+ public static String typeArgumentsName(TypeInfo[] args, HashSet<String> typeVars)
+ {
+ String result = "<";
+ for (int i=0; i<args.length; i++) {
+ result += args[i].fullName(typeVars);
+ if (i != args.length-1) {
+ result += ", ";
+ }
+ }
+ result += ">";
+ return result;
+ }
+
+ public String fullName(HashSet<String> typeVars)
+ {
+ mFullName = fullNameNoDimension(typeVars) + mDimension;
+ return mFullName;
+ }
+
+ public String fullNameNoDimension(HashSet<String> typeVars)
+ {
+ String fullName = null;
+ if (mIsTypeVariable) {
+ if (typeVars.contains(mQualifiedTypeName)) {
+ // don't recurse forever with the parameters. This handles
+ // Enum<K extends Enum<K>>
+ return mQualifiedTypeName;
+ }
+ typeVars.add(mQualifiedTypeName);
+ }
+/*
+ if (fullName != null) {
+ return fullName;
+ }
+*/
+ fullName = mQualifiedTypeName;
+ if (mTypeArguments != null && mTypeArguments.length > 0) {
+ fullName += typeArgumentsName(mTypeArguments, typeVars);
+ }
+ else if (mSuperBounds != null && mSuperBounds.length > 0) {
+ fullName += " super " + mSuperBounds[0].fullName(typeVars);
+ for (int i=1; i<mSuperBounds.length; i++) {
+ fullName += " & " + mSuperBounds[i].fullName(typeVars);
+ }
+ }
+ else if (mExtendsBounds != null && mExtendsBounds.length > 0) {
+ fullName += " extends " + mExtendsBounds[0].fullName(typeVars);
+ for (int i=1; i<mExtendsBounds.length; i++) {
+ fullName += " & " + mExtendsBounds[i].fullName(typeVars);
+ }
+ }
+ return fullName;
+ }
+
+ public TypeInfo[] typeArguments()
+ {
+ return mTypeArguments;
+ }
+
+ public void makeHDF(HDF data, String base)
+ {
+ makeHDFRecursive(data, base, false, false, new HashSet<String>());
+ }
+
+ public void makeQualifiedHDF(HDF data, String base)
+ {
+ makeHDFRecursive(data, base, true, false, new HashSet<String>());
+ }
+
+ public void makeHDF(HDF data, String base, boolean isLastVararg,
+ HashSet<String> typeVariables)
+ {
+ makeHDFRecursive(data, base, false, isLastVararg, typeVariables);
+ }
+
+ public void makeQualifiedHDF(HDF data, String base, HashSet<String> typeVariables)
+ {
+ makeHDFRecursive(data, base, true, false, typeVariables);
+ }
+
+ private void makeHDFRecursive(HDF data, String base, boolean qualified,
+ boolean isLastVararg, HashSet<String> typeVars)
+ {
+ String label = qualified ? qualifiedTypeName() : simpleTypeName();
+ label += (isLastVararg) ? "..." : dimension();
+ data.setValue(base + ".label", label);
+ ClassInfo cl = asClassInfo();
+ if (mIsTypeVariable || mIsWildcard) {
+ // could link to an @param tag on the class to describe this
+ // but for now, just don't make it a link
+ }
+ else if (!isPrimitive() && cl != null && cl.isIncluded()) {
+ data.setValue(base + ".link", cl.htmlPage());
+ }
+
+ if (mIsTypeVariable) {
+ if (typeVars.contains(qualifiedTypeName())) {
+ // don't recurse forever with the parameters. This handles
+ // Enum<K extends Enum<K>>
+ return;
+ }
+ typeVars.add(qualifiedTypeName());
+ }
+ if (mTypeArguments != null) {
+ TypeInfo.makeHDF(data, base + ".typeArguments", mTypeArguments, qualified, typeVars);
+ }
+ if (mSuperBounds != null) {
+ TypeInfo.makeHDF(data, base + ".superBounds", mSuperBounds, qualified, typeVars);
+ }
+ if (mExtendsBounds != null) {
+ TypeInfo.makeHDF(data, base + ".extendsBounds", mExtendsBounds, qualified, typeVars);
+ }
+ }
+
+ public static void makeHDF(HDF data, String base, TypeInfo[] types, boolean qualified,
+ HashSet<String> typeVariables)
+ {
+ final int N = types.length;
+ for (int i=0; i<N; i++) {
+ types[i].makeHDFRecursive(data, base + "." + i, qualified, false, typeVariables);
+ }
+ }
+
+ public static void makeHDF(HDF data, String base, TypeInfo[] types, boolean qualified)
+ {
+ makeHDF(data, base, types, qualified, new HashSet<String>());
+ }
+
+ void setTypeArguments(TypeInfo[] args)
+ {
+ mTypeArguments = args;
+ }
+
+ void setBounds(TypeInfo[] superBounds, TypeInfo[] extendsBounds)
+ {
+ mSuperBounds = superBounds;
+ mExtendsBounds = extendsBounds;
+ }
+
+ void setIsTypeVariable(boolean b)
+ {
+ mIsTypeVariable = b;
+ }
+
+ void setIsWildcard(boolean b)
+ {
+ mIsWildcard = b;
+ }
+
+ static HashSet<String> typeVariables(TypeInfo[] params)
+ {
+ return typeVariables(params, new HashSet());
+ }
+
+ static HashSet<String> typeVariables(TypeInfo[] params, HashSet<String> result)
+ {
+ for (TypeInfo t: params) {
+ if (t.mIsTypeVariable) {
+ result.add(t.mQualifiedTypeName);
+ }
+ }
+ return result;
+ }
+
+
+ public boolean isTypeVariable()
+ {
+ return mIsTypeVariable;
+ }
+
+ public String defaultValue() {
+ if (mIsPrimitive) {
+ if ("boolean".equals(mSimpleTypeName)) {
+ return "false";
+ } else {
+ return "0";
+ }
+ } else {
+ return "null";
+ }
+ }
+
+ public String toString(){
+ String returnString = "";
+ returnString += "Primitive?: " + mIsPrimitive + " TypeVariable?: " +
+ mIsTypeVariable + " Wildcard?: " + mIsWildcard + " Dimension: " + mDimension
+ + " QualifedTypeName: " + mQualifiedTypeName;
+
+ if (mTypeArguments != null){
+ returnString += "\nTypeArguments: ";
+ for (TypeInfo tA : mTypeArguments){
+ returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+ }
+ }
+ if (mSuperBounds != null){
+ returnString += "\nSuperBounds: ";
+ for (TypeInfo tA : mSuperBounds){
+ returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+ }
+ }
+ if (mExtendsBounds != null){
+ returnString += "\nExtendsBounds: ";
+ for (TypeInfo tA : mExtendsBounds){
+ returnString += tA.qualifiedTypeName() + "(" + tA + ") ";
+ }
+ }
+ return returnString;
+ }
+
+ private boolean mIsPrimitive;
+ private boolean mIsTypeVariable;
+ private boolean mIsWildcard;
+ private String mDimension;
+ private String mSimpleTypeName;
+ private String mQualifiedTypeName;
+ private ClassInfo mClass;
+ private TypeInfo[] mTypeArguments;
+ private TypeInfo[] mSuperBounds;
+ private TypeInfo[] mExtendsBounds;
+ private String mFullName;
+}
diff --git a/tools/droiddoc/templates-sdk/assets-sdk/placeholder b/tools/droiddoc/templates-sdk/assets-sdk/placeholder
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/assets-sdk/placeholder
diff --git a/tools/droiddoc/templates-sdk/customization.cs b/tools/droiddoc/templates-sdk/customization.cs
new file mode 100644
index 000000000..4a1dde6fe
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/customization.cs
@@ -0,0 +1,121 @@
+<?cs # This default template file is meant to be replaced. ?>
+<?cs # Use the -tempatedir arg to javadoc to set your own directory with a replacement for this file in it. ?>
+
+<?cs
+def:custom_masthead() ?>
+ <div id="header">
+ <div id="headerLeft">
+ <a href="<?cs var:toroot ?>index.html" tabindex="-1"><img
+ src="<?cs var:toroot ?>assets/images/bg_logo.png" alt="Android Developers" /></a>
+ <ul class="<?cs
+ if:reference ?>reference<?cs
+ elif:guide ?>guide<?cs
+ elif:sdk ?>sdk<?cs
+ elif:home ?>home<?cs
+ elif:community ?>community<?cs
+ elif:publish ?>publish<?cs
+ elif:about ?>about<?cs /if ?>">
+ <li id="home-link"><a href="<?cs var:toroot ?><?cs
+ if:android.whichdoc != "online" ?>offline.html<?cs
+ else ?>index.html<?cs /if ?>">
+ <span>Home</span></a></li>
+ <li id="sdk-link"><a href="<?cs var:toroot ?>sdk/1.1_r1/index.html"><span>SDK</span></a></li>
+ <li id="guide-link"><a href="<?cs var:toroot ?>guide/index.html"
+ onClick="return loadLast('guide')"><span>Dev Guide</span></a></li>
+ <li id="reference-link"><a href="<?cs var:toroot ?>reference/packages.html"
+ onClick="return loadLast('reference')"><span>Reference</span></a></li>
+ <li><a href="http://android-developers.blogspot.com"><span>Blog</span></a></li>
+ <li id="community-link"><a href="<?cs var:toroot ?>community/index.html"><span>Community</span></a></li>
+ </ul>
+ </div>
+ <div id="headerRight">
+ <div id="headerLinks">
+ <!-- <img src="<?cs var:toroot ?>assets/images/icon_world.jpg" alt="" /> -->
+ <span class="text">
+ <!-- &nbsp;<a href="#">English</a> | -->
+ <a href="http://www.android.com">Android.com</a>
+ </span>
+ </div><?cs
+ call:default_search_box() ?>
+ </div><!-- headerRight -->
+ </div><!-- header --><?cs
+/def ?><?cs # custom_masthead ?>
+
+<?cs
+def:sdk_nav() ?>
+ <div class="g-section g-tpl-240" id="body-content">
+ <div class="g-unit g-first not-resizable" id="side-nav">
+ <div id="devdoc-nav"><?cs
+ include:"../../../../frameworks/base/docs/html/sdk/sdk_toc.cs" ?>
+ </div>
+ </div> <!-- end side-nav -->
+<?cs /def ?>
+
+<?cs
+def:guide_nav() ?>
+ <div class="g-section g-tpl-240" id="body-content">
+ <div class="g-unit g-first side-nav-resizable" id="side-nav">
+ <div id="devdoc-nav"><?cs
+ include:"../../../../frameworks/base/docs/html/guide/guide_toc.cs" ?>
+ </div>
+ </div> <!-- end side-nav -->
+ <script>
+ addLoadEvent(function() {
+ scrollIntoView("devdoc-nav");
+ });
+ </script>
+<?cs /def ?>
+
+<?cs
+def:publish_nav() ?>
+ <div class="g-section g-tpl-180" id="body-content">
+ <div class="g-unit g-first" id="side-nav">
+ <div id="devdoc-nav"><?cs
+ include:"../../../../frameworks/base/docs/html/publish/publish_toc.cs" ?>
+ </div>
+ </div> <!-- end side-nav -->
+<?cs /def ?>
+
+<?cs
+def:custom_left_nav() ?><?cs
+ if:guide ?><?cs
+ call:guide_nav() ?><?cs
+ elif:publish ?><?cs
+ call:publish_nav() ?><?cs
+ elif:sdk ?><?cs
+ call:sdk_nav() ?><?cs
+ else ?><?cs
+ call:default_left_nav() ?><?cs
+ /if ?><?cs
+/def ?>
+
+<?cs # appears at the bottom of every page ?><?cs
+def:custom_cc_copyright() ?>
+ Except as noted, this content is
+ licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+ Creative Commons Attribution 2.5</a>. For details and
+ restrictions, see the <a href="<?cs var:toroot ?>license.html">Content
+ License</a>.<?cs
+/def ?>
+
+<?cs
+def:custom_copyright() ?>
+ Except as noted, this content is licensed under <a
+ href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>.
+ For details and restrictions, see the <a href="<?cs var:toroot ?>license.html">
+ Content License</a>.<?cs
+/def ?>
+
+<?cs
+def:custom_footerlinks() ?>
+ <p>
+ <a href="http://www.android.com/terms.html">Site Terms of Service</a> -
+ <a href="http://www.android.com/privacy.html">Privacy Policy</a> -
+ <a href="http://www.android.com/branding.html">Brand Guidelines</a>
+ </p><?cs
+/def ?>
+
+<?cs # appears on the right side of the blue bar at the bottom of every page ?><?cs
+def:custom_buildinfo() ?>
+ Android 1.1 r1 - <?cs var:page.now ?><?cs
+/def ?>
diff --git a/tools/droiddoc/templates-sdk/data.hdf b/tools/droiddoc/templates-sdk/data.hdf
new file mode 100644
index 000000000..9411b7871
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/data.hdf
@@ -0,0 +1,4 @@
+template {
+ which = normal
+}
+
diff --git a/tools/droiddoc/templates-sdk/devdoc-nav.cs b/tools/droiddoc/templates-sdk/devdoc-nav.cs
new file mode 100644
index 000000000..a69c175af
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/devdoc-nav.cs
@@ -0,0 +1,66 @@
+<ul>
+ <li><div><a href="<?cs var:toroot ?>index.html">Home</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>what-is-android.html">What is Android?</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/index.html">Getting Started</a></div>
+ <ul>
+ <li><div><a href="<?cs var:toroot ?>intro/installing.html">Installing the SDK</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/upgrading.html">Upgrading the SDK</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/develop-and-debug.html">Developing/Debugging</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/hello-android.html">Hello Android</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/anatomy.html">Anatomy of an App</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/tutorial.html">Notepad Tutorial</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/tools.html">Development Tools</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/appmodel.html">Application Model</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>intro/lifecycle.html">Application Life Cycle</a></div></li>
+ </ul>
+ </li>
+ <li><div><div><a href="<?cs var:toroot ?>devel/index.html">Developing Applications</a></div>
+ <ul>
+ <li><div><a href="<?cs var:toroot ?>devel/implementing-ui.html">Implementing a UI</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>devel/building-blocks.html">Building Blocks</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>devel/data.html">Data Storage and Retrieval</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>devel/security.html">Security Model</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>devel/resources-i18n.html">Resources and i18n</a></div></li>
+ </ul>
+ </li>
+ <li><div><a href="<?cs var:toroot ?>toolbox/index.html">Developer Toolbox</a></div>
+ <ul>
+ <li><div><a href="<?cs var:toroot ?>toolbox/philosophy.html">Design Philosophy</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>toolbox/custom-components.html">Building Custom Components</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>toolbox/optional-apis.html">Optional APIs</a></div></li>
+ </ul>
+ </li>
+ <li><div><a href="<?cs var:toroot ?>samples/index.html">Sample Code</a></div>
+ <ul>
+ <li><div><a href="<?cs var:toroot ?>samples/ApiDemos/index.html">API Demos</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>samples/LunarLander/index.html">Lunar Lander</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>samples/NotePad/index.html">Note Pad</a></div></li>
+ </ul>
+ </li>
+ <li> <a href="<?cs var:toroot ?>reference/index.html"><strong>Reference Information</strong></a>
+ <ul>
+ <li><a href="<?cs var:toroot ?>reference/packages.html">Package Index</a></li>
+ <li><a href="<?cs var:toroot ?>reference/classes.html">Class Index</a></li>
+ <li><a href="<?cs var:toroot ?>reference/hierarchy.html">Class Hierarchy</a></li>
+ <li><a href="<?cs var:toroot ?>reference/view-gallery.html">List of Views</a></li>
+ <li><a href="<?cs var:toroot ?>reference/available-intents.html">List of Intents</a></li>
+ <li><a href="<?cs var:toroot ?>reference/android/Manifest.permission.html">List of Permissions</a></li>
+ <li><a href="<?cs var:toroot ?>reference/available-resources.html">List of Resource Types</a></li>
+ <li><a href="<?cs var:toroot ?>reference/aidl.html">Android IDL</a></li>
+ <li><a href="<?cs var:toroot ?>reference/glossary.html">Glossary</a></li>
+ <li><a href="<?cs var:toroot ?>reference/keywords.html">Index</a></li>
+ </ul>
+ </li>
+ <li><div><a href="<?cs var:toroot ?>kb/index.html">FAQs</a></div>
+ <ul>
+ <li><div><a href="<?cs var:toroot ?>kb/general.html">General</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>kb/commontasks.html">Common Tasks</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>kb/troubleshooting.html">Troubleshooting</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>kb/licensingandoss.html">Open Source Licensing</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>kb/framework.html">Application Framework</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>kb/security.html">Security</a></div></li>
+ </ul>
+ </li>
+ <li><div><a href="<?cs var:toroot ?>roadmap.html">Roadmap</a></div></li>
+ <li><div><a href="<?cs var:toroot ?>goodies/index.html">Goodies</a></div></li>
+</ul> \ No newline at end of file
diff --git a/tools/droiddoc/templates-sdk/sdkpage.cs b/tools/droiddoc/templates-sdk/sdkpage.cs
new file mode 100644
index 000000000..b425da0df
--- /dev/null
+++ b/tools/droiddoc/templates-sdk/sdkpage.cs
@@ -0,0 +1,108 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs if:sdk.redirect ?>
+ <head>
+ <title>Redirecting...</title>
+ <meta http-equiv="refresh" content="0;url=<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
+ <link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" />
+ </head>
+<?cs else ?>
+ <?cs include:"head_tag.cs" ?>
+<?cs /if ?>
+<body class="gc-documentation">
+<a name="top"></a>
+<?cs call:custom_masthead() ?>
+
+<?cs call:sdk_nav() ?>
+
+
+<div class="g-unit" id="doc-content" >
+
+<?cs if:sdk.redirect ?>
+ Redirecting to
+ <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html">
+ <?cs var:toroot ?>sdk/<?cs var:sdk.redirect ?>/index.html
+ </a>...
+<?cs else ?>
+
+ <div id="jd-header" class="guide-header" >
+ <span class="crumb">&nbsp;</span>
+ <h1><?cs if:android.whichdoc == "online" ?>Download <?cs /if ?><?cs var:page.title ?></h1>
+ </div>
+
+
+<div id="jd-content">
+
+ <p><em>
+ <?cs var:sdk.date ?>
+ </em></p>
+
+<?cs if:sdk.not_latest_version ?>
+ <div class="special">
+ <p><strong>This is NOT the current Android SDK release.</strong></p>
+ <p>Use the links under <strong>Current SDK Release</strong>, on the left, to be directed to the current SDK.</p>
+ </div>
+<?cs /if ?>
+
+
+<?cs if:android.whichdoc != "online" ?>
+
+<p>The sections below provide an overview of the SDK package. </p>
+
+<?cs else ?>
+
+<p>Before downloading, please read the <a href="<?cs var:toroot ?>sdk/<?cs var:sdk.version ?>/requirements.html">
+System Requirements</a> document. As you start the download, you will also need to review and agree to
+the Terms and Conditions that govern the use of the Android SDK. </p>
+
+ <table class="download">
+ <tr>
+ <th>Platform</th>
+ <th>Package</th>
+ <th>Size</th>
+ <th>MD5 Checksum</th>
+ </tr>
+ <tr>
+ <td>Windows</td>
+ <td>
+ <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.win_download ?>"><?cs var:sdk.win_download ?></a>
+ </td>
+ <td><?cs var:sdk.win_bytes ?> bytes</td>
+ <td><?cs var:sdk.win_checksum ?></td>
+ </tr>
+ <tr class="alt-color">
+ <td>Mac OS X (intel)</td>
+ <td>
+ <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.mac_download ?>"><?cs var:sdk.mac_download ?></a>
+ </td>
+ <td><?cs var:sdk.mac_bytes ?> bytes</td>
+ <td><?cs var:sdk.mac_checksum ?></td>
+ </tr>
+ <tr>
+ <td>Linux (i386)</td>
+ <td>
+ <a href="<?cs var:toroot ?>sdk/download.html?v=<?cs var:sdk.linux_download ?>"><?cs var:sdk.linux_download ?></a>
+ </td>
+ <td><?cs var:sdk.linux_bytes ?> bytes</td>
+ <td><?cs var:sdk.linux_checksum ?></td>
+ </tr>
+ </table>
+
+<?cs /if ?>
+
+ <?cs call:tag_list(root.descr) ?>
+
+<?cs /if ?>
+</div><!-- end jd-content -->
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/assets/android-developer-core.css b/tools/droiddoc/templates/assets/android-developer-core.css
new file mode 100644
index 000000000..eaa117627
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-core.css
@@ -0,0 +1,909 @@
+/* file: android-developer-core.css
+ author: smain
+ date: september 2008
+ info: core developer styles (developer.android.com)
+*/
+
+
+/* RESET STYLES */
+
+html,body,div,h1,h2,h3,h4,h5,h6,p,img,
+dl,dt,dd,ol,ul,li,table,caption,tbody,
+tfoot,thead,tr,th,td,form,fieldset,
+embed,object,applet {
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+/* BASICS */
+
+html, body {
+ overflow:hidden; /* keeps scrollbar off IE */
+ background-color:#fff;
+}
+
+body {
+ font-family:arial,sans-serif;
+ color:#000;
+ font-size:13px;
+ color:#333;
+ background-image:url(images/bg_fade.jpg);
+ background-repeat:repeat-x;
+}
+
+a, a code {
+ color:#006699;
+}
+
+
+a:active,
+a:active code {
+ color:#f00;
+}
+
+a:visited,
+a:visited code {
+ color:#006699;
+}
+
+input, select,
+textarea, option {
+ font-family:inherit;
+ font-size:inherit;
+}
+
+p {
+ padding:0;
+ margin:0 0 1em;
+}
+
+code, pre {
+ color:#007000;
+ font-family:monospace;
+ line-height:1em;
+}
+
+var {
+ color:#007000;
+ font-style:italic;
+}
+
+pre {
+ border:1px solid #ccc;
+ background-color:#fafafa;
+ padding:10px;
+ margin:0 0 1em 1em;
+ overflow:auto;
+}
+
+h1,h2,h3,h4,h5 {
+ margin:1em 0;
+ padding:0;
+}
+
+p,ul,ol,dl,dd,dt,li {
+ line-height:1.3em;
+}
+
+ul,ol {
+ margin:0 0 .8em;
+ padding:0 0 0 2em;
+}
+
+li {
+ padding:0 0 .5em;
+}
+
+dl {
+ margin:0 0 1em 0;
+ padding:0;
+}
+
+dt {
+ margin:0;
+ padding:0;
+}
+
+dd {
+ margin:0 0 1em;
+ padding:0 0 0 2em;
+}
+
+li p, dd p {
+ margin:1em 0 0;
+}
+
+li pre, li table, li img,
+dd pre, dd table, dd img {
+ margin:1em 0 0 1em;
+}
+
+li ul,
+li ol {
+ margin:.5em 0 0 0;
+ padding: 0 0 0 2em;
+}
+
+dl li {
+ padding:.5em 0 0 0;
+}
+
+dl dl,
+ol dl,
+ul dl {
+ margin:0 0 1em;
+ padding:0;
+}
+
+table {
+ font-size:1em;
+ margin:0 0 1em;
+ padding:0;
+ border-collapse:collapse;
+ border-width:0;
+ empty-cells:show;
+}
+
+td,th {
+ border:1px solid #ccc;
+ padding:6px 12px;
+ text-align:left;
+ vertical-align:top;
+ background-color:inherit;
+}
+
+th {
+ background-color:#dee8f1;
+}
+
+hr.blue {
+ background-color:#DDF0F2;
+ border:none;
+ height:5px;
+ margin:20px 0 10px;
+}
+
+/* LAYOUT */
+#body-content {
+ margin:0;
+ position:relative;
+ width:100%;
+ background: url('images/preliminary.png');
+}
+
+#header {
+ height: 114px;
+ position:relative;
+ z-index:100;
+ min-width:576px;
+ padding:0 10px;
+ border-bottom:3px solid #94b922;
+}
+
+#headerLeft{
+ padding: 25px 0 0;
+}
+
+#headerRight {
+ position:absolute;
+ right:0;
+ top:0;
+ text-align:right;
+}
+
+/* Tabs in the header */
+#header ul {
+ list-style: none;
+ margin: 7px 0 0;
+ padding: 0;
+ height: 29px;
+}
+
+#header li {
+ float: left;
+ margin: 0px 2px 0px 0px;
+ padding:0;
+}
+
+#header li a {
+ text-decoration: none;
+ display: block;
+ background-image: url(images/bg_images_sprite.png);
+ background-position: 0 -58px;
+ background-repeat: no-repeat;
+ color: #666;
+ font-size: 13px;
+ font-weight: bold;
+ width: 94px;
+ height: 29px;
+ text-align: center;
+ margin: 0px;
+}
+
+#header li a:hover {
+ background-image: url(images/bg_images_sprite.png);
+ background-position: 0 -29px;
+ background-repeat: no-repeat;
+}
+
+#header li a span {
+ position:relative;
+ top:7px;
+}
+
+/* TAB HIGHLIGHTING */
+.home #home-link a,
+.publish #publish-link a,
+.guide #guide-link a,
+.reference #reference-link a,
+.sdk #sdk-link a,
+.community #community-link a,
+.about #about-link a {
+ background-image: url(images/bg_images_sprite.png);
+ background-position: 0 0;
+ background-repeat: no-repeat;
+ color: #fff;
+ font-weight: bold;
+ cursor:default;
+}
+
+.home #home-link a:hover,
+.publish #publish-link a:hover,
+.guide #guide-link a:hover,
+.reference #reference-link a:hover,
+.sdk #sdk-link a:hover,
+.community #community-link a:hover,
+.about #about-link a:hover {
+ background-image: url(images/bg_images_sprite.png);
+ background-position: 0 0;
+}
+
+#headerLinks {
+ margin:10px 10px 0 0;
+ height:13px;
+}
+
+#headerLinks .text {
+ text-decoration: none;
+ color: #7FA9B5;
+ font-size: 11px;
+ vertical-align: top;
+}
+
+#headerLinks a {
+ text-decoration: underline;
+ color: #7FA9B5;
+ font-size: 11px;
+ vertical-align: top;
+}
+
+#search {
+ height:45px;
+ margin:15px 10px 0 0;
+}
+
+/* main */
+
+#mainBodyFluid {
+ margin: 20px 10px;
+ color:#333;
+}
+
+#mainBodyFixed {
+ margin: 20px 10px;
+ color: #333;
+ width:930px;
+ position:relative;
+}
+
+#mainBodyFixed h3,
+#mainBodyFluid h3 {
+ color:#336666;
+ font-size:1.25em;
+ margin: 0em 0em 0em 0em;
+ padding-bottom:.5em;
+}
+
+#mainBodyFixed h2,
+#mainBodyFluid h2 {
+ color:#336666;
+ font-size:1.25em;
+ margin: 0;
+ padding-bottom:.5em;
+}
+
+#mainBodyFixed h1,
+#mainBodyFluid h1 {
+ color:#435A6E;
+ font-size:1.7em;
+ margin: 1em 0;
+}
+
+#mainBodyFixed .green,
+#mainBodyFluid .green,
+#jd-content .green {
+ color:#7BB026;
+ background-color:none;
+}
+
+#mainBodyLeft {
+ float: left;
+ width: 600px;
+ margin-right: 20px;
+ color: #333;
+ position:relative;
+}
+
+div.indent {
+ margin-left: 40px;
+ margin-right: 70px;
+}
+
+#mainBodyLeft p {
+ color: #333;
+ font-size: 13px;
+}
+
+#mainBodyLeft p.blue {
+ color: #669999;
+}
+
+#mainBodyLeft #communityDiv {
+ float: left;
+ background-image:url(images/bg_community_leftDiv.jpg);
+ background-repeat: no-repeat;
+ width: 581px;
+ height: 347px;
+ padding: 20px 0px 0px 20px;
+}
+
+#mainBodyRight {
+ float: left;
+ width: 300px;
+ color: #333;
+}
+
+#mainBodyRight p {
+ padding-right: 50px;
+ color: #333;
+}
+
+#mainBodyRight table {
+ width: 100%;
+}
+
+#mainBodyRight td {
+ border:0px solid #666;
+ padding:0px 5px;
+ text-align:left;
+}
+
+#mainBodyRight .blueBorderBox {
+ border:5px solid #ddf0f2;
+ padding:18px 18px 18px 18px;
+ text-align:left;
+}
+
+#mainBodyFixed .seperator {
+ background-image:url(images/hr_gray_side.jpg);
+ background-repeat:no-repeat;
+ width: 100%;
+ float: left;
+ clear: both;
+}
+
+#mainBodyBottom {
+ float: left;
+ width: 100%;
+ clear:both;
+ color: #333;
+}
+
+#mainBodyBottom .seperator {
+ background-image:url(images/hr_gray_main.jpg);
+ background-repeat:no-repeat;
+ width: 100%;
+ float: left;
+ clear: both;
+}
+
+/* Footer */
+#footer {
+ float: left;
+ width:90%;
+ margin: 20px;
+ color: #aaa;
+ font-size: 11px;
+}
+
+#footer a {
+ color: #aaa;
+ font-size: 11px;
+}
+
+#footer a:hover {
+ text-decoration: underline;
+ color:#aaa;
+}
+
+#footerlinks {
+ margin-top:2px;
+}
+
+#footerlinks a,
+#footerlinks a:visited {
+ color:#006699;
+}
+
+#homeBottom td {
+ border:0px solid #666;
+ padding: 8px 18px 8px 18px;
+}
+
+#homeBottom table {
+ width: 100%;
+}
+
+
+#homeBottom {
+ padding: 0px 0px 0px 0px;
+ float: left;
+ width: 585px;
+ height: 165px;
+ background-image:url(images/home/bg_home_bottom.jpg);
+ background-repeat: no-repeat;
+}
+
+.groupTable {
+ width: 100%;
+}
+
+.groupTable th {
+ padding: 10px;
+ color: #ffffff;
+ background-color: #6D8293;
+ border: 2px solid #fff;
+}
+
+.groupTable td {
+ padding: 10px;
+ color: #333333;
+ background-color: #d9d9d9;
+ border: 2px solid #fff;
+}
+
+.groupTable .evenRow td {
+ background-color: #ededed;
+}
+
+span.BigBlue {
+ color:#336666;
+ font-size:1.25em;
+ margin: 0em 0em 0em 0em;
+ padding-bottom:.5em;
+ font-weight: bold;
+}
+
+span.emBlue {
+ color: #336666;
+ font-style:italic;
+}
+
+.pageTable {
+ width: 95%;
+ border: none;
+}
+
+.pageTable img {
+vertical-align: bottom;
+}
+
+.pageTable td {
+ border: none;
+}
+
+.pageTable td.leftNav {
+ width: 100px;
+}
+
+.greenBox {
+ margin: 10px 30px 10px 30px;
+ padding: 10px 20px 10px 20px;
+ background-color: #EBF3DB;
+ width: 75%;
+}
+
+.blueBox {
+ margin: 10px 30px 10px 30px;
+ padding: 10px 20px 10px 20px;
+ background-color: #DDF0F2;
+ width: 75%;
+}
+
+.blueHR {
+ margin: 10px 30px 10px 30px;
+ height: 5px;
+ background-color: #DDF0F2;
+ width: 75%;
+}
+
+/* SEARCH FILTER */
+#search_autocomplete {
+ color:#aaa;
+}
+
+#search-button {
+ display:inline;
+}
+
+#search_filtered_div {
+ position:absolute;
+ margin-top:-1px;
+ z-index:101;
+ border:1px solid #BCCDF0;
+ background-color:#fff;
+}
+
+#search_filtered {
+ min-width:100%;
+}
+#search_filtered td{
+ background-color:#fff;
+ border-bottom: 1px solid #669999;
+ line-height:1.5em;
+}
+
+#search_filtered .jd-selected {
+ background-color: #94b922;
+ cursor:pointer;
+}
+#search_filtered .jd-selected,
+#search_filtered .jd-selected a {
+ color:#fff;
+}
+
+.no-display {
+ display: none;
+}
+
+.jd-autocomplete {
+ font-family: Arial, sans-serif;
+ padding-left: 6px;
+ padding-right: 6px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ font-size: .8em;
+ border: none;
+ margin: 0;
+ line-height: 1.05em;
+}
+
+.show-row {
+ display: table-row;
+}
+.hide-row {
+ display: hidden;
+}
+
+/* SEARCH */
+
+/* restrict global search form width */
+#searchForm {
+ width:350px;
+}
+
+#searchTxt {
+ width:200px;
+}
+
+/* disable twiddle and size selectors for left column */
+#leftSearchControl div {
+ width: 100%;
+}
+
+#leftSearchControl .gsc-twiddle {
+ background-image : none;
+}
+
+#leftSearchControl td, #searchForm td {
+ border: 0px solid #000;
+}
+
+#leftSearchControl .gsc-resultsHeader .gsc-title {
+ padding-left : 0px;
+ font-weight : bold;
+ font-size : 13px;
+ color:#006699;
+ display : none;
+}
+
+#leftSearchControl .gsc-resultsHeader div.gsc-results-selector {
+ display : none;
+}
+
+#leftSearchControl .gsc-resultsRoot {
+ padding-top : 6px;
+}
+
+#leftSearchControl div.gs-visibleUrl-long {
+ display : block;
+ color:#006699;
+}
+
+.gsc-webResult div.gs-visibleUrl-short,
+table.gsc-branding,
+.gsc-clear-button {
+ display : none;
+}
+
+.gsc-cursor-box .gsc-cursor div.gsc-cursor-page,
+.gsc-cursor-box .gsc-trailing-more-results a.gsc-trailing-more-results,
+#leftSearchControl a,
+#leftSearchControl a b {
+ color:#006699;
+}
+
+.gsc-resultsHeader {
+ display: none;
+}
+
+/* Disable built in search forms */
+.gsc-control form.gsc-search-box {
+ display : none;
+}
+table.gsc-search-box {
+ margin:6px 0 0 0;
+ border-collapse:collapse;
+}
+
+td.gsc-input {
+ padding:0 2px;
+ width:100%;
+ vertical-align:middle;
+}
+
+input.gsc-input {
+ border:1px solid #BCCDF0;
+ width:99%;
+ padding-left:2px;
+ font-size:.95em;
+}
+
+td.gsc-search-button {
+ text-align: right;
+ padding:0;
+ vertical-align:top;
+}
+
+#search-button {
+ margin:0 0 0 2px;
+ font-size:11px;
+ height:1.8em;
+}
+
+/* search result tabs */
+
+#doc-content .gsc-control {
+ position:relative;
+}
+
+#doc-content .gsc-tabsArea {
+ position:relative;
+}
+
+#doc-content .gsc-tabHeader {
+ padding: 3px 6px;
+ position:relative;
+}
+
+#doc-content .gsc-tabHeader.gsc-tabhActive {
+ border-top: 2px solid #94B922;
+}
+
+#doc-content h2#searchTitle {
+ padding:0;
+}
+
+#doc-content .gsc-resultsbox-visible {
+ padding:1em 0 0 6px;
+}
+
+/* CAROUSEL */
+
+#homeMiddle {
+ padding: 0px 0px 0px 0px;
+ float: left;
+ width: 584px;
+ height: 580px;
+ background:url(images/home/bg_home_middle.png) no-repeat 0 0;
+ position:relative;
+}
+
+#homeTitle {
+ margin:15px 15px 0;
+ height:30px;
+ background:url(images/hr_gray_side.jpg) no-repeat 0 29px;
+}
+
+#homeTitle h2 {
+ padding:0;
+}
+
+#announcement-block {
+ margin:15px 15px 0;
+ height:125px;
+}
+
+#announcement-block img {
+ float:left;
+ margin:0 30px 0 0;
+}
+
+#announcement {
+ float:left;
+ margin:0;
+}
+
+.clearer { clear:both; }
+
+#arrow-left, #arrow-right {
+ float:left;
+ width:42px;
+ height:42px;
+ background-image:url(images/home/carousel_buttons_sprite.png);
+ background-repeat:no-repeat;
+}
+#arrow-left {
+ margin:35px 3px 0 10px;
+}
+#arrow-right {
+ margin:35px 10px 0 0;
+}
+.arrow-left-off,
+#arrow-left.arrow-left-off:hover {
+ background-position:0 0;
+}
+.arrow-right-off,
+#arrow-right.arrow-right-off:hover {
+ background-position:-42px 0;
+}
+#arrow-left:hover {
+ background-position:0 -42px;
+}
+#arrow-right:hover {
+ background-position:-42px -42px;
+}
+.arrow-left-on {
+ background-position:0 0;
+}
+.arrow-right-on {
+ background-position:-42px 0;
+}
+.arrow-right-off,
+.arrow-left-off {
+ cursor:default;
+}
+
+.app-list-container {
+ margin:37px 20px 0;
+ _margin-top:33px;
+ position:relative;
+ width:100%;
+}
+
+div#list-clip {
+ height:110px;
+ width:438px;
+ overflow:hidden;
+ position:relative;
+ float:left;
+}
+
+div#app-list {
+ left:0;
+ z-index:1;
+ position:absolute;
+ margin:11px 0 0;
+ _margin-top:13px;
+ width:1000%;
+}
+
+#app-list a {
+ display:block;
+ float:left;
+ height:90px;
+ width:90px;
+ margin:0 24px 0;
+ padding:3px;
+ background:#99cccc;
+ -webkit-border-radius:7px;
+ -moz-border-radius:7px;
+ border-radius:7px;
+ text-decoration:none;
+ text-align:center;
+ font-size:11px;
+}
+
+#app-list img {
+ width:90px;
+ height:70px;
+ margin:0;
+}
+
+#app-list a.selected,
+#app-list a:active.selected,
+#app-list a:hover.selected {
+ background:#A4C639;
+ color:#fff;
+ cursor:default;
+ text-decoration:none;
+}
+
+#app-list a:hover,
+#app-list a:active {
+ background:#ff9900;
+}
+
+#app-list a:hover span,
+#app-list a:active span {
+ text-decoration:underline;
+}
+
+#droid-name {
+ padding-top:.5em;
+ color:#666;
+ padding-bottom:.25em;
+}
+
+#carouselMain {
+ margin: 25px 21px 0;
+ height:185px;
+ background-position:top;
+ background-repeat:no-repeat;
+ overflow:hidden;
+}
+
+#carouselMain img {
+ margin:0;
+}
+
+/*carousel bulletin layouts*/
+/*460px width*/
+/*185px height*/
+.img-left {
+ float:left;
+ width:230px;
+ height:165px;
+ overflow:hidden;
+ margin:8px 0 8px 8px;
+}
+.desc-right {
+ float:left;
+ width:270px;
+ margin:10px;
+}
+.img-right {
+ float:right;
+ width:220px;
+ height:165px;
+ overflow:hidden;
+ margin:8px 8px 8px 0;
+}
+.desc-left {
+ float:right;
+ width:280px;
+ margin:10px;
+ text-align:right;
+}
+.img-top {
+ height:80px;
+ text-align:center;
+}
+.desc-bottom {
+ height:100px;
+ margin:10px;
+}
+
+
+
diff --git a/tools/droiddoc/templates/assets/android-developer-docs-devguide.css b/tools/droiddoc/templates/assets/android-developer-docs-devguide.css
new file mode 100644
index 000000000..d8bd3b347
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs-devguide.css
@@ -0,0 +1,19 @@
+
+@import url("android-developer-docs.css");
+
+/* Page title */
+
+#jd-header h1 {
+ padding: 8px 0 0 0;
+}
+
+/* Page content container */
+
+#jd-header table {
+margin: 0 0 1em 1em;
+}
+
+#jd-content table table,
+#jd-content table img {
+ margin:1em 0;
+} \ No newline at end of file
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.css b/tools/droiddoc/templates/assets/android-developer-docs.css
new file mode 100644
index 000000000..daa839fb8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs.css
@@ -0,0 +1,1083 @@
+/* file: android-developer-docs.css
+ author: smain
+ date: september 2008
+ info: developer doc styles (developer.android.com)
+*/
+
+@import url("android-developer-core.css");
+
+#title {
+ border-bottom: 4px solid #ccc;
+ display:none;
+}
+
+#title h1 {
+ color:#336666;
+ margin:0;
+ padding: 5px 10px;
+ font-size: 1em;
+ line-height: 15px;
+}
+
+#title h1 .small{
+ color:#000;
+ margin:0;
+ font-size: 13px;
+ padding:0 0 0 15px;
+}
+
+/* SIDE NAVIGATION */
+
+#side-nav {
+ padding:0 6px 0 0;
+ background-color: #fff;
+ font-size:12px;
+}
+
+#side-nav.not-resizable {
+ background:url('images/sidenav-rule.png') no-repeat 243px 0;
+}
+
+#resize-packages-nav {
+/* keeps the resize handle below the h-scroll handle */
+ height:270px;
+ overflow:hidden;
+ max-height:100%;
+}
+
+#packages-nav {
+ height:270px;
+ max-height:inherit;
+ position:relative;
+ overflow:auto;
+}
+
+#classes-nav,
+#devdoc-nav {
+ overflow:auto;
+ position:relative;
+}
+
+#side-nav ul {
+ list-style: none;
+ margin: 0;
+ padding:5px 0;
+}
+
+#side-nav ul ul {
+ margin: .35em 0 0 0;
+ padding: 0;
+}
+
+#side-nav li {
+ padding:0;
+ line-height:16px;
+ white-space:nowrap;
+ zoom:1;
+}
+
+#side-nav li h2 {
+ font-size:12px;
+ font-weight: bold;
+ margin:.5em 0 0 0;
+ padding: 3px 0 1px 9px;
+}
+
+#side-nav li a {
+ text-decoration:none;
+ padding: 0 0 0 18px;
+ zoom:1;
+}
+
+#side-nav li a:hover {
+ text-decoration:underline;
+}
+
+#side-nav li a+a {
+ padding: 0;
+}
+
+#side-nav li li li a {
+/*sdk lists*/
+ padding: 0 0 0 28px;
+}
+
+#side-nav .selected {
+ background-color: #435a6e;
+ color: #fff;
+ font-weight:bold;
+}
+
+#side-nav .selected a {
+ color: #fff;
+ text-decoration:none;
+}
+
+#side-nav strong {
+ display:block;
+}
+
+#side-nav .toggle-img {
+ margin:0;
+ padding:0;
+ position:absolute;
+ top:0;
+ left:0;
+ height:16px;
+ width:15px;
+ outline-style:none;
+}
+
+#side-nav .closed .toggle-img {
+ background:url('images/triangle-closed-small.png') 7px 4px no-repeat;
+}
+#side-nav .open .toggle-img {
+ background:url('images/triangle-opened-small.png') 7px 4px no-repeat;
+}
+
+#side-nav .toggle-list {
+ position:relative;
+}
+
+#side-nav .toggle-list ul {
+ margin:0;
+ display:none;
+}
+
+#side-nav .toggle-list div {
+ display:block;
+}
+
+#index-links .selected {
+ background-color: #fff;
+ color: #000;
+ font-weight:normal;
+ text-decoration:none;
+}
+
+#index-links {
+ padding:7px 0 4px 10px;
+}
+
+/* nav tree */
+
+#nav-tree ul {
+ padding:5px 0 1.5em;
+}
+
+#side-nav #nav-tree ul li a,
+#side-nav #nav-tree ul li span.no-children {
+ padding: 0 0 0 0;
+ margin: 0;
+}
+
+#nav-tree .plus {
+ margin: 0 3px 0 0;
+}
+
+#nav-tree ul ul {
+ list-style: none;
+ margin: 0;
+ padding: 0 0 0 0;
+}
+
+#nav-tree ul li {
+ margin: 0;
+ padding: 0 0 0 0;
+ white-space: nowrap;
+}
+
+#nav-tree .children_ul {
+ margin:0;
+}
+
+#nav-tree a.nolink {
+ color: black;
+ text-decoration: none;
+}
+
+#nav-tree span.label {
+ width: 100%;
+}
+
+#nav-tree {
+ overflow-x: auto;
+ overflow-y: scroll;
+}
+
+#nav-swap {
+ font-size:10px;
+ line-height:10px;
+ margin-left:1em;
+ text-decoration:none;
+ display:block;
+}
+
+#tree-link {
+
+}
+
+/* DOCUMENT BODY */
+
+#doc-content {
+ overflow:auto;
+}
+
+#jd-header {
+ background-color: #E2E2E2;
+ padding: 7px 15px;
+}
+
+#jd-header h1 {
+ margin: 0 0 10px;
+ font-size:1.7em;
+}
+
+#jd-header .crumb {
+ font-size:.9em;
+ line-height:1em;
+ color:#777;
+}
+
+#jd-header .crumb a,
+#jd-header .crumb a:visited {
+ text-decoration:none;
+ color:#777;
+}
+
+#jd-header .crumb a:hover {
+ text-decoration:underline;
+}
+
+#jd-header table {
+ margin:0;
+ padding:0;
+}
+
+#jd-header td {
+ border:none;
+ padding:0;
+ vertical-align:top;
+}
+
+#jd-header.guide-header {
+ background-color:#fff;
+ color:#435a6e;
+ height:50px;
+}
+
+#jd-descr {
+ position:relative;
+}
+
+/* summary tables for reference pages */
+.jd-sumtable {
+margin: .5em 1em 1em 1em;
+width:99%;
+font-size:.9em;
+}
+
+.jd-sumtable a {
+ text-decoration:none;
+}
+
+.jd-sumtable a:hover {
+ text-decoration:underline;
+}
+
+/* the link inside a sumtable for "Show All/Hide All" */
+.toggle-all {
+ display:block;
+ float:right;
+ font-weight:normal;
+ font-size:0.9em;
+}
+
+/* adjustments for in/direct subclasses tables */
+.jd-sumtable-subclasses {
+ margin: 1em 0 0 0;
+ max-width:968px;
+}
+
+/* extra space between end of method name and open-paren */
+.sympad {
+ margin-right: 2px;
+}
+
+/* right alignment for the return type in sumtable */
+.jd-sumtable .jd-typecol {
+ text-align:right;
+}
+
+/* adjustments for the expando table-in-table */
+.jd-sumtable-expando {
+ margin:.5em 0;
+ padding:0;
+}
+
+/* a div that holds a short description */
+.jd-descrdiv {
+ width:100%;
+ padding:3px 1em 0 1em;
+ margin:0;
+ border:0;
+}
+
+/* page-top-right container for reference pages (holds
+links to summary tables) */
+#api-info-block {
+ font-size:.8em;
+ margin:0;
+ padding:6px;
+ font-weight:normal;
+ float:right;
+ text-align:right;
+ color:#999;
+ max-width:70%;
+}
+
+/* applies to a div containing links to summary tables */
+.sum-details-links {
+ margin:0 .5em;
+ padding:0;
+ font-weight:normal;
+}
+
+.sum-details-links a {
+ text-decoration:none;
+}
+
+.sum-details-links a:hover {
+ text-decoration:underline;
+}
+
+
+/* inheritance table */
+.jd-inheritance-table {
+ border-spacing:0;
+ margin:0;
+ padding:0;
+ font-size:.9em;
+}
+.jd-inheritance-table td {
+ border: none;
+ margin: 0;
+ padding: 0;
+}
+.jd-inheritance-table .jd-inheritance-space {
+ font-weight:bold;
+ width:1em;
+}
+.jd-inheritance-table .jd-inheritance-interface-cell {
+ padding-left: 17px;
+}
+
+#jd-content {
+ padding: 18px 15px;
+}
+
+hr {
+ background-color:#ccc;
+}
+
+/* DOC CLASSES */
+
+#jd-content h1 {
+/*sdk page*/
+ font-size:1.6em;
+ color:#336666;
+ margin:0 0 .5em;
+}
+
+#jd-content h2 {
+ font-size:1.45em;
+ color:#111;
+ border-top:2px solid #ccc;
+ padding: .5em 0 0;
+ margin: 1.5em 0 1em 0;
+ max-width:968px;
+}
+
+#jd-content h3 {
+ font-size:1.2em;
+ color:#222;
+ padding: .75em 0 .65em 0;
+ margin:0;
+}
+
+#jd-content h4 {
+ font-size:1.1em;
+ margin-bottom:.5em;
+ color:#222;
+}
+
+#jd-content .small-header {
+ font-size:1em;
+ color:#000;
+ font-weight:bold;
+ border:none;
+ padding:0;
+ margin:1em 0 .5em;
+ position:inherit;
+}
+
+#jd-content img {
+ margin: 0 0 1em 1em;
+}
+
+#jd-content li img,
+#jd-content dd img {
+ margin:.5em 0 0 1em;
+}
+
+.nolist {
+ list-style:none;
+ padding:0;
+ margin:0 0 0 1em;
+}
+
+.nolist li {
+ padding:0;
+ margin:0;
+}
+
+h4 .normal {
+ font-size:.9em;
+ font-weight:normal;
+}
+
+.jd-details {
+/* border:1px solid #669999;
+ padding:4px; */
+ margin:0 0 1em;
+}
+
+/* API reference: a container for the
+.tagdata blocks that make up the detailed
+description */
+.jd-details-descr {
+ padding:0;
+ margin:.5em .25em;
+}
+
+/* API reference: a block containing
+a detailed description, a params table,
+seealso list, etc */
+.jd-tagdata {
+ margin:.5em 1em;
+}
+
+/* API reference: adjustments to
+the detailed description block */
+.jd-tagdescr {
+ margin:.25em 0 .75em 0;
+ line-height:1em;
+}
+
+.jd-tagdescr p {
+ margin:.5em 0;
+ padding:0;
+
+}
+
+.jd-tagdescr ol,
+.jd-tagdescr ul {
+ margin:0 2.5em;
+ padding:0;
+}
+
+.jd-tagdescr table,
+.jd-tagdescr img {
+ margin:.25em 1em;
+}
+
+.jd-tagdescr li {
+margin:0 0 .25em 0;
+padding:0;
+}
+
+/* API reference: heading marking
+the details section for constants,
+attrs, methods, etc. */
+h4.jd-details-title {
+ font-size:1.15em;
+ background-color: #E2E2E2;
+ margin:1.5em 0 .6em;
+ padding:3px;
+}
+
+h4.jd-tagtitle {
+ margin:0;
+}
+
+/* API reference: heading for "Parameters", "See Also", etc.,
+in details sections */
+h5.jd-tagtitle {
+ margin:0 0 .25em 0;
+ font-size:1em;
+}
+
+.jd-tagtable {
+ margin:0;
+}
+
+.jd-tagtable td,
+.jd-tagtable th {
+ border:none;
+ background-color:#fff;
+ vertical-align:top;
+ font-weight:normal;
+ padding:2px 10px;
+}
+
+.jd-tagtable th {
+ font-style:italic;
+}
+
+#jd-content table h2 {
+ background-color: #d6d6d6;
+ font-size: 1.1em;
+ margin:0 0 10px;
+ padding:5px;
+ left:0;
+ width:auto;
+}
+
+div.special {
+ padding: .5em 1em 1em 1em;
+ margin: 0 0 1em;
+ background-color: #ddf0f2;
+}
+
+div.special p {
+ margin: .5em 0 0 0;
+}
+
+div.special ol {
+ margin: 0;
+}
+
+div.special ol li {
+ margin: 0;
+ padding: 0;
+}
+
+#jd-content div.special h2,
+#jd-content div.special h3 {
+ color:#669999;
+ font-size:1.2em;
+ border:none;
+ margin:0 0 .5em;
+ padding:0;
+}
+
+/* old p.note, p.caution, p.warning {
+ margin:0 0 1em;
+ padding: 4px 10px;
+ background-color: #efefef;
+ border-top: 1px solid;
+ border-bottom: 1px solid;
+}
+*/
+p.note, p.caution, p.warning {
+ margin: 1em;
+ padding: 0 0 0 .5em;
+ border-left: 4px solid;
+}
+
+p.special-note {
+ background-color:#EBF3DB;
+ padding:10px 20px;
+ margin:0 0 1em;
+}
+
+p.note {
+ border-color: #99aacc;
+}
+
+p.caution {
+ border-color: #ffcc33;
+}
+
+p.warning {
+ border-color: #aa0033;
+}
+
+p.warning b, p.warning em, p.warning strong {
+ color: #aa0033;
+ font-weight: bold;
+}
+
+li p.note, li p.warning, li p.caution {
+ margin: .5em 0 0 0;
+ padding: .2em .5em .2em .9em;
+}
+
+dl.xml dt {
+ font-variant:small-caps;
+ font-size:1.2em;
+}
+
+dl.xml dl {
+ padding:0;
+}
+
+dl.xml dl dt {
+ font-variant:normal;
+ font-size:1em;
+}
+
+.listhead li {
+ font-weight: bold;
+}
+
+.listhead li *, /*ie*/.listhead li li {
+ font-weight: normal;
+}
+
+ol.no-style,
+ul.no-style {
+ list-style:none;
+ padding-left:1em;
+}
+
+.new {
+ font-size: .78em;
+ font-weight: bold;
+ color: red;
+ text-decoration: none;
+}
+
+pre.classic {
+ background-color:transparent;
+ border:none;
+ padding:0;
+}
+
+
+/* BEGIN quickview sidebar element styles */
+
+#qv-wrapper {
+ float: right;
+ width:310px;
+ background-color:#fff;
+ margin:-48px 0 2px 0;
+ padding:0 0 20px 35px;
+}
+
+#qv {
+ background-color:#fff;
+ border:4px solid #dee8f1;
+ margin:0;
+ padding:0 6px 6px;
+ width:270px;
+ font-size:.9em;
+}
+
+#qv ol {
+ list-style:none;
+ padding: 0;
+}
+
+#qv ol ol{
+ list-style:none;
+ padding: 0 0 3px 12px;
+ margin:0;
+}
+
+#qv ul {
+ padding: 0 10px 0 2em;
+}
+
+#qv li {
+ padding: 0 10px;
+ margin: 2 0 0;
+ line-height: 1.2em;
+}
+
+#qv ul li {
+ padding: 0 10px 0 0;
+}
+
+#qv li.selected a {
+ color:#555;
+ text-decoration:none;
+}
+
+#qv a {
+ color:#cc6600;
+}
+
+#qv p {
+ margin:8px 0 0;
+ padding:0 10px;
+}
+
+#qv-extra #rule {
+ padding: 0 10px;
+ margin: 0;
+}
+
+#qv-sub-rule {
+ padding: 6px 20px;
+ margin: 0;
+}
+
+#qv-sub-rule p {
+ margin: 0;
+}
+
+#jd-content #qv h2 {
+ font-size:1.05em;
+ font-weight:bold;
+ margin:12px 0 .25em 0;
+ padding:0 10px;
+ background-color:transparent;
+ color:#7BB026;
+ border:none;
+ left:0;
+ z-index:1;
+}
+
+/* END quickview sidebar element styles */
+
+/* Begin sidebox sidebar element styles */
+
+.sidebox-wrapper {
+ float: right;
+ width:280px;
+ background-color:#fff;
+ margin: 0;
+ padding: 20px 0 20px 20px;
+}
+
+.sidebox-inner {
+ border-left:1px solid #dee8f1;
+ background-color:#ffffee;
+ padding:5px 8px 5px 12px;
+ font-size:90%;
+ width:260px;
+}
+
+.sidebox {
+ float: right;
+ width:260px;
+ background-color:#ffffee;
+ border-left:1px solid #dee8f1;
+ margin: 12px 0 0 15px;
+ padding:5px 8px 0 12px;
+ font-size:90%;
+}
+
+.sidebox p,
+.sidebox-inner p {
+ margin-bottom: .25em;
+}
+
+.sidebox ul,
+.sidebox-inner ul {
+ padding: 0 0 0 1.5em;
+}
+
+.sidebox li ul,
+.sidebox-inner li ul {
+ margin-top:0;
+ margin-bottom:.1em;
+}
+
+.sidebox li,
+.sidebox-inner li {
+padding:0 0 0 0em;
+}
+
+#jd-content .sidebox h2,
+#jd-content .sidebox h3,
+#jd-content .sidebox-inner h2,
+#jd-content .sidebox-inner h3 {
+ border:none;
+ font-size:1em;
+ margin:0;
+ padding:4px 0 4px;
+ left:0;
+ z-index:0;
+}
+
+.sidebox hr,
+.sidebox-inner hr {
+ background-color:#ccc;
+ border:none;
+}
+
+/* End sidebox sidebar element styles */
+
+/* table of contents */
+
+ol.toc {
+ margin: 0 0 1em 0;
+ padding: 0;
+ list-style: none;
+ font-size:95%;
+}
+
+ol.toc li {
+ font-weight: bold;
+ margin: 0 0 .5em 1em;
+ padding: 0;
+}
+
+ol.toc li p {
+ font-weight: normal;
+}
+
+ol.toc li ol {
+ margin: 0;
+ padding: 0;
+}
+
+ol.toc li li {
+ padding: 0;
+ margin: 0 0 0 1em;
+ font-weight: normal;
+ list-style: none;
+}
+
+table ol.toc {
+ margin-left: 0;
+}
+
+.columns td {
+ padding:0 5px;
+ border:none;
+}
+
+/* link table */
+.jd-linktable {
+ margin: 0 0 1em;
+ border-bottom: 1px solid #888;
+}
+.jd-linktable th,
+.jd-linktable td {
+ padding: 3px 5px;
+ vertical-align: top;
+ text-align: left;
+ border:none;
+}
+.jd-linktable tr {
+ background-color: #fff;
+}
+.jd-linktable td {
+ border-top: 1px solid #888;
+ background-color: inherit;
+}
+.jd-linktable td p {
+ padding: 0 0 5px;
+}
+.jd-linktable .jd-linkcol {
+}
+.jd-linktable .jd-descrcol {
+}
+.jd-linktable .jd-typecol {
+ text-align:right;
+}
+.jd-linktable .jd-valcol {
+}
+.jd-linktable .jd-commentrow {
+ border-top:none;
+ padding-left:25px;
+}
+.jd-deprecated-warning {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+
+tr.alt-color {
+ background-color: #f6f6f6;
+}
+
+/* expando trigger */
+#jd-content .jd-expando-trigger-img {
+ margin:0;
+}
+
+/* jd-expando */
+.jd-inheritedlinks {
+ padding:0 0 0 13px
+}
+
+/* SDK PAGE */
+table.download tr {
+ background-color:#d9d9d9;
+}
+
+table.download tr.alt-color {
+ background-color:#ededed;
+}
+
+table.download td,
+table.download th {
+ border:2px solid #fff;
+ padding:10px 5px;
+}
+
+table.download th {
+ background-color:#6d8293;
+ color:#fff;
+}
+
+/* INLAY 180 COPY and 240PX EXTENSION */
+/* modified to 43px so that all browsers eliminate the package panel h-scroll */
+.g-tpl-240 .g-unit,
+.g-unit .g-tpl-240 .g-unit,
+.g-unit .g-unit .g-tpl-240 .g-unit {
+ display: block;
+ margin: 0 0 0 243px;
+ width: auto;
+ float: none;
+}
+.g-unit .g-unit .g-tpl-240 .g-first,
+.g-unit .g-tpl-240 .g-first,
+.g-tpl-240 .g-first {
+ display: block;
+ margin: 0;
+ width: 243px;
+ float: left;
+}
+/* 240px alt */
+.g-tpl-240-alt .g-unit,
+.g-unit .g-tpl-240-alt .g-unit,
+.g-unit .g-unit .g-tpl-240-alt .g-unit {
+ display: block;
+ margin: 0 243px 0 0;
+ width: auto;
+ float: none;
+}
+.g-unit .g-unit .g-tpl-240-alt .g-first,
+.g-unit .g-tpl-240-alt .g-first,
+.g-tpl-240-alt .g-first {
+ display: block;
+ margin: 0;
+ width: 243px;
+ float: right;
+}
+
+/* 180px */
+.g-tpl-180 .g-unit,
+.g-unit .g-tpl-180 .g-unit,
+.g-unit .g-unit .g-tpl-180 .g-unit {
+ display: block;
+ margin: 0 0 0 180px;
+ width: auto;
+ float: none;
+}
+.g-unit .g-unit .g-tpl-180 .g-first,
+.g-unit .g-tpl-180 .g-first,
+.g-tpl-180 .g-first {
+ display: block;
+ margin: 0;
+ width: 180px;
+ float: left;
+}
+/* 180px alt */
+.g-tpl-180-alt .g-unit,
+.g-unit .g-tpl-180-alt .g-unit,
+.g-unit .g-unit .g-tpl-180-alt .g-unit {
+ display: block;
+ margin: 0 180px 0 0;
+ width: auto;
+ float: none;
+}
+.g-unit .g-unit .g-tpl-180-alt .g-first,
+.g-unit .g-tpl-180-alt .g-first,
+.g-tpl-180-alt .g-first {
+ display: block;
+ margin: 0;
+ width: 180px;
+ float: right;
+}
+
+
+/* JQUERY RESIZABLE STYLES */
+.ui-resizable { position: relative; }
+.ui-resizable-handle { position: absolute; display: none; font-size: 0.1px; z-index:1; }
+.ui-resizable .ui-resizable-handle { display: block; }
+body .ui-resizable-disabled .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+body .ui-resizable-autohide .ui-resizable-handle { display: none; } /* use 'body' to make it more specific (css order) */
+.ui-resizable-s { cursor: s-resize; height: 6px; width: 100%; bottom: 0px; left: 0px; background: transparent url("images/resizable-s2.gif") repeat scroll center top; }
+.ui-resizable-e { cursor: e-resize; width: 6px; right: 0px; top: 0px; height: 100%; background: transparent url("images/resizable-e2.gif") repeat scroll right center; }
+
+@media print {
+
+ body {
+ overflow:visible;
+ }
+
+ #header {
+ height:60px;
+ }
+
+ #headerLeft {
+ margin:0;
+ }
+
+ #headerRight {
+ display:none;
+ }
+
+ #body-content {
+ position:inherit;
+ }
+
+ #side-nav {
+ display:none;
+ }
+
+ #doc-content {
+ margin-left:0 !important;
+ height:auto !important;
+ width:auto !important;
+ overflow:inherit;
+ display:inline;
+ }
+
+ #jd-header {
+ padding:10px 0;
+ }
+
+ #jd-content {
+ padding:15px 0 0;
+ }
+
+ #footer {
+ float:none;
+ margin:2em 0 0;
+ }
+
+ h4.jd-details-title {
+ border-bottom:1px solid #666;
+ }
+
+ pre {
+ /* these allow lines to break (if there's a white space) */
+ overflow: visible;
+ text-wrap: unrestricted;
+ white-space: -moz-pre-wrap; /* Moz */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ white-space: pre-wrap; /* CSS3 */
+ word-wrap: break-word; /* IE 5.5+ */
+ }
+
+ h1, h2, h3, h4, h5, h6 {
+ page-break-after: avoid;
+ }
+
+ table, img {
+ page-break-inside: avoid;
+ }
+
+ #qv,
+ #qv-wrapper {
+ display:none;
+ }
+
+}
diff --git a/tools/droiddoc/templates/assets/android-developer-docs.js b/tools/droiddoc/templates/assets/android-developer-docs.js
new file mode 100644
index 000000000..a84d5a639
--- /dev/null
+++ b/tools/droiddoc/templates/assets/android-developer-docs.js
@@ -0,0 +1,330 @@
+var resizePackagesNav;
+var classesNav;
+var devdocNav;
+var sidenav;
+var content;
+var HEADER_HEIGHT = 117;
+var cookie_style = 'android_developer';
+var NAV_PREF_TREE = "tree";
+var NAV_PREF_PANELS = "panels";
+var nav_pref;
+var toRoot;
+
+
+function addLoadEvent(newfun) {
+ var current = window.onload;
+ if (typeof window.onload != 'function') {
+ window.onload = newfun;
+ } else {
+ window.onload = function() {
+ current();
+ newfun();
+ }
+ }
+}
+
+window.onresize = resizeAll;
+
+function setToRoot(root) {
+ toRoot = root;
+ // note: toRoot also used by carousel.js
+}
+
+function restoreWidth(navWidth) {
+ var windowWidth = $(window).width() + "px";
+ content.css({marginLeft:parseInt(navWidth) + 6 + "px", //account for 6px-wide handle-bar
+ width:parseInt(windowWidth) - parseInt(navWidth) - 6 + "px"});
+ sidenav.css({width:navWidth});
+ resizePackagesNav.css({width:navWidth});
+ classesNav.css({width:navWidth});
+ $("#packages-nav").css({width:navWidth});
+}
+
+function restoreHeight(packageHeight) {
+ var windowHeight = ($(window).height() - HEADER_HEIGHT);
+ var swapperHeight = windowHeight - 13;
+ $("#swapper").css({height:swapperHeight + "px"});
+ sidenav.css({height:windowHeight + "px"});
+ content.css({height:windowHeight + "px"});
+ resizePackagesNav.css({maxHeight:swapperHeight + "px", height:packageHeight});
+ classesNav.css({height:swapperHeight - parseInt(packageHeight) + "px"});
+ $("#packages-nav").css({height:parseInt(packageHeight) - 6 + "px"}); //move 6px to give space for the resize handle
+ devdocNav.css({height:sidenav.css("height")});
+ $("#nav-tree").css({height:swapperHeight + "px"});
+}
+
+function getCookie(cookie) {
+ var myCookie = cookie_style+"_"+cookie+"=";
+ if (document.cookie) {
+ var index = document.cookie.indexOf(myCookie);
+ if (index != -1) {
+ var valStart = index + myCookie.length;
+ var valEnd = document.cookie.indexOf(";", valStart);
+ if (valEnd == -1) {
+ valEnd = document.cookie.length;
+ }
+ var val = document.cookie.substring(valStart, valEnd);
+ return val;
+ }
+ }
+ return 0;
+}
+
+function writeCookie(cookie, val, path, expiration) {
+ if (!val) return;
+ var date = new Date();
+ date.setTime(date.getTime()+(10*365*24*60*60*1000)); // default expiration is one week
+ expiration = expiration ? expiration : date.toGMTString();
+ if (location.href.indexOf("/reference/") != -1) {
+ document.cookie = cookie_style+'_reference_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
+ } else if (location.href.indexOf("/guide/") != -1) {
+ document.cookie = cookie_style+'_guide_'+cookie+'='+val+'; expires='+expiration+'; path='+'/'+path;
+ }
+}
+
+function init() {
+ $("#resize-packages-nav").resizable({handles: "s", resize: function(e, ui) { resizeHeight(); } });
+ $(".side-nav-resizable").resizable({handles: "e", resize: function(e, ui) { resizeWidth(); } });
+
+ $("#side-nav").css({position:"absolute",left:0});
+ content = $("#doc-content");
+ resizePackagesNav = $("#resize-packages-nav");
+ classesNav = $("#classes-nav");
+ sidenav = $("#side-nav");
+ devdocNav = $("#devdoc-nav");
+
+ if (location.href.indexOf("/reference/") != -1) {
+ var cookiePath = "reference_";
+ } else if (location.href.indexOf("/guide/") != -1) {
+ var cookiePath = "guide_";
+ }
+ var cookieWidth = getCookie(cookiePath+'width');
+ var cookieHeight = getCookie(cookiePath+'height');
+ if (cookieWidth) {
+ restoreWidth(cookieWidth);
+ } else if ($(".side-nav-resizable").length) {
+ resizeWidth();
+ }
+ if (cookieHeight) {
+ restoreHeight(cookieHeight);
+ } else {
+ resizeHeight();
+ }
+
+ if (devdocNav.length) { // only dev guide and sdk
+ highlightNav(location.href);
+ }
+}
+
+function highlightNav(fullPageName) {
+ var lastSlashPos = fullPageName.lastIndexOf("/");
+ var firstSlashPos = (fullPageName.indexOf("/guide/") != -1) ?
+ fullPageName.indexOf("/guide/") :
+ fullPageName.indexOf("/sdk/"); // first slash after /guide or /sdk
+ if (lastSlashPos == (fullPageName.length - 1)) { // if the url ends in slash (add 'index.html')
+ fullPageName = fullPageName + "index.html";
+ }
+ var htmlPos = fullPageName.lastIndexOf(".html", fullPageName.length);
+ var pathPageName = fullPageName.slice(firstSlashPos, htmlPos + 5);
+ var link = $("#devdoc-nav a[href$='"+ pathPageName+"']");
+ if ((link.length == 0) && ((fullPageName.indexOf("/guide/") != -1) || (fullPageName.indexOf("/sdk/") != -1))) {
+// if there's no match, then let's backstep through the directory until we find an index.html page that matches our ancestor directories (only for dev guide and sdk)
+ lastBackstep = pathPageName.lastIndexOf("/");
+ while (link.length == 0) {
+ backstepDirectory = pathPageName.lastIndexOf("/", lastBackstep);
+ link = $("#devdoc-nav a[href$='"+ pathPageName.slice(0, backstepDirectory + 1)+"index.html']");
+ lastBackstep = pathPageName.lastIndexOf("/", lastBackstep - 1);
+ if (lastBackstep == 0) break;
+ }
+ }
+ link.parent().addClass('selected');
+ if (link.parent().parent().is(':hidden')) {
+ toggle(link.parent().parent().parent(), false);
+ } else if (link.parent().parent().hasClass('toggle-list')) {
+ toggle(link.parent().parent(), false);
+ }
+}
+
+function resizeHeight() {
+ var windowHeight = ($(window).height() - HEADER_HEIGHT);
+ var swapperHeight = windowHeight - 13;
+ $("#swapper").css({height:swapperHeight + "px"});
+ sidenav.css({height:windowHeight + "px"});
+ content.css({height:windowHeight + "px"});
+ resizePackagesNav.css({maxHeight:swapperHeight + "px"});
+ classesNav.css({height:swapperHeight - parseInt(resizePackagesNav.css("height")) + "px"});
+ $("#packages-nav").css({height:parseInt(resizePackagesNav.css("height")) - 6 + "px"}); //move 6px for handle
+ devdocNav.css({height:sidenav.css("height")});
+ $("#nav-tree").css({height:swapperHeight + "px"});
+ writeCookie("height", resizePackagesNav.css("height"), "", null);
+}
+
+function resizeWidth() {
+ var windowWidth = $(window).width() + "px";
+ if (sidenav.length) {
+ var sidenavWidth = sidenav.css("width");
+ } else {
+ var sidenavWidth = 0;
+ }
+ content.css({marginLeft:parseInt(sidenavWidth) + 6 + "px", //account for 6px-wide handle-bar
+ width:parseInt(windowWidth) - parseInt(sidenavWidth) - 6 + "px"});
+ resizePackagesNav.css({width:sidenavWidth});
+ classesNav.css({width:sidenavWidth});
+ $("#packages-nav").css({width:sidenavWidth});
+ writeCookie("width", sidenavWidth, "", null);
+}
+
+function resizeAll() {
+ resizeHeight();
+ if ($(".side-nav-resizable").length) {
+ resizeWidth();
+ }
+}
+
+function loadLast(cookiePath) {
+ var location = window.location.href;
+ if (location.indexOf("/"+cookiePath+"/") != -1) {
+ return true;
+ }
+ var lastPage = getCookie(cookiePath + "_lastpage");
+ if (lastPage) {
+ window.location = lastPage;
+ return false;
+ }
+ return true;
+}
+
+$(window).unload(function(){
+ var href = location.href;
+ if (href.indexOf("/reference/") != -1) {
+ writeCookie("lastpage", href, "", null);
+ } else if (href.indexOf("/guide/") != -1) {
+ writeCookie("lastpage", href, "", null);
+ }
+});
+
+
+
+function toggle(obj, slide) {
+ var ul = $("ul", obj);
+ var li = ul.parent();
+ if (li.hasClass("closed")) {
+ if (slide) {
+ ul.slideDown("fast");
+ } else {
+ ul.show();
+ }
+ li.removeClass("closed");
+ li.addClass("open");
+ $(".toggle-img", li).attr("title", "hide pages");
+ } else {
+ ul.slideUp("fast");
+ li.removeClass("open");
+ li.addClass("closed");
+ $(".toggle-img", li).attr("title", "show pages");
+ }
+}
+
+
+
+function buildToggleLists() {
+ $(".toggle-list").each(
+ function(i) {
+ $("div", this).append("<a class='toggle-img' href='#' title='show pages' onClick='toggle(this.parentNode.parentNode, true); return false;'></a>");
+ $(this).addClass("closed");
+ });
+}
+
+function getNavPref() {
+ var v = getCookie('reference_nav');
+ if (v != NAV_PREF_TREE) {
+ v = NAV_PREF_PANELS;
+ }
+ return v;
+}
+
+function chooseDefaultNav() {
+ nav_pref = getNavPref();
+ if (nav_pref == NAV_PREF_TREE) {
+ $("#nav-panels").toggle();
+ $("#panel-link").toggle();
+ $("#nav-tree").toggle();
+ $("#tree-link").toggle();
+ }
+}
+
+function swapNav() {
+ if (nav_pref == NAV_PREF_TREE) {
+ nav_pref = NAV_PREF_PANELS;
+ } else {
+ nav_pref = NAV_PREF_TREE;
+ init_navtree("nav-tree", toRoot, NAVTREE_DATA);
+ }
+ var date = new Date();
+ date.setTime(date.getTime()+(10*365*24*60*60*1000)); // keep this for 10 years
+ writeCookie("nav", nav_pref, "", date.toGMTString());
+
+ $("#nav-panels").toggle();
+ $("#panel-link").toggle();
+ $("#nav-tree").toggle();
+ $("#tree-link").toggle();
+
+ if ($("#nav-tree").is(':visible')) scrollIntoView("nav-tree");
+ else {
+ scrollIntoView("packages-nav");
+ scrollIntoView("classes-nav");
+ }
+}
+
+function scrollIntoView(nav) {
+ var navObj = $("#"+nav);
+ if (navObj.is(':visible')) {
+ var selected = $(".selected", navObj);
+ if (selected.length == 0) return;
+ if (selected.is("div")) selected = selected.parent();
+
+ var scrolling = document.getElementById(nav);
+ var navHeight = navObj.height();
+ var offsetTop = selected.position().top;
+ if (selected.parent().parent().is(".toggle-list")) offsetTop += selected.parent().parent().position().top;
+ if(offsetTop > navHeight - 92) {
+ scrolling.scrollTop = offsetTop - navHeight + 92;
+ }
+ }
+}
+
+function toggleAllInherited(linkObj, expand) {
+ var a = $(linkObj);
+ var table = $(a.parent().parent().parent());
+ var expandos = $(".jd-expando-trigger", table);
+ if ( (expand == null && a.text() == "[Expand]") || expand ) {
+ expandos.each(function(i) {
+ toggleInherited(this, true);
+ });
+ a.text("[Collapse]");
+ } else if ( (expand == null && a.text() == "[Collapse]") || (expand == false) ) {
+ expandos.each(function(i) {
+ toggleInherited(this, false);
+ });
+ a.text("[Expand]");
+ }
+ return false;
+}
+
+function toggleAllSummaryInherited(linkObj) {
+ var a = $(linkObj);
+ var content = $(a.parent().parent().parent());
+ var toggles = $(".toggle-all", content);
+ if (a.text() == "[Expand All]") {
+ toggles.each(function(i) {
+ toggleAllInherited(this, true);
+ });
+ a.text("[Collapse All]");
+ } else {
+ toggles.each(function(i) {
+ toggleAllInherited(this, false);
+ });
+ a.text("[Expand All]");
+ }
+ return false;
+}
diff --git a/tools/droiddoc/templates/assets/carousel.js b/tools/droiddoc/templates/assets/carousel.js
new file mode 100644
index 000000000..4eebd8911
--- /dev/null
+++ b/tools/droiddoc/templates/assets/carousel.js
@@ -0,0 +1,295 @@
+/* file: carousel.js
+ date: oct 2008
+ author: jeremydw,smain
+ info: operates the carousel widget for announcements on
+ the android developers home page. modified from the
+ original market.js from jeremydw. */
+
+/* -- video switcher -- */
+
+var oldVid = "multi"; // set the default video
+var nowPlayingString = "Now playing:";
+var assetsRoot = "assets/";
+
+
+/* -- app thumbnail switcher -- */
+
+var currentDroid;
+var oldDroid;
+
+// shows a random application
+function randomDroid(){
+
+ // count the total number of apps
+ var droidListLength = 0;
+ for (var k in droidList)
+ droidListLength++;
+
+ // pick a random app and show it
+ var j = 0;
+ var i = Math.floor(droidListLength*Math.random());
+ for (var x in droidList) {
+ if(j++ == i){
+ currentDroid = x;
+ showPreview(x);
+ centerSlide(x);
+ }
+ }
+
+}
+
+// shows a bulletin, swaps the carousel highlighting
+function droid(appName){
+
+ oldDroid = $("#droidlink-"+currentDroid);
+ currentDroid = appName;
+
+ var droid = droidList[appName];
+ var layout = droid.layout;
+ var imgDiv = document.getElementById("bulletinImg");
+ var descDiv = document.getElementById("bulletinDesc");
+
+ if (layout == "imgLeft") {
+ imgDiv.className = "img-left";
+ descDiv.className = "desc-right";
+ } else if (layout == "imgTop") {
+ imgDiv.className = "img-top";
+ descDiv.className = "desc-bottom";
+ } else if (layout == "imgRight") {
+ imgDiv.className = "img-right";
+ descDiv.className = "desc-left";
+ }
+
+ imgDiv.innerHTML = "<img src='" + toRoot + assetsRoot + "images/home/" + droid.img + "'>";
+ descDiv.innerHTML = (droid.title != "") ? "<h3>" + droid.title + "</h3>" + droid.desc : droid.desc;
+
+ if(oldDroid)
+ oldDroid.removeClass("selected");
+
+ $("#droidlink-"+appName).addClass("selected");
+}
+
+
+// -- * build the carousel based on the droidList * -- //
+function buildCarousel() {
+ var appList = document.getElementById("app-list");
+ for (var x in droidList) {
+ var droid = droidList[x];
+ var icon = droid.icon;
+ var name = droid.name;
+ var a = document.createElement("a");
+ var img = document.createElement("img");
+ var br = document.createElement("br");
+ var span = document.createElement("span");
+ var text = document.createTextNode(droid.name);
+
+ a.setAttribute("id", "droidlink-" + x);
+ a.className = x;
+ a.setAttribute("href", "#");
+ a.onclick = function() { showPreview(this.className); return false; }
+ img.setAttribute("src", toRoot + assetsRoot + "images/home/" + droid.icon);
+ img.setAttribute("alt", "");
+
+ span.appendChild(text);
+ a.appendChild(img);
+ a.appendChild(br);
+ a.appendChild(span);
+ appList.appendChild(a);
+ }
+}
+
+// -- * slider * -- //
+
+// -- dependencies:
+// (1) div containing slides, (2) a "clip" div to hide the scroller
+// (3) control arrows
+
+// -- * config below * -- //
+
+var slideCode = droidList; // the dictionary of slides
+var slideList = 'app-list'; // the div containing the slides
+var arrowRight = 'arrow-right'; // the right control arrow
+var arrowLeft = 'arrow-left'; // the left control arrow
+
+
+function showPreview(slideName) {
+ centerSlide(slideName);
+ if (slideName.indexOf('selected') != -1) {
+ return false;
+ }
+ droid(slideName); // do this function when slide is clicked
+}
+
+var thumblist = document.getElementById(slideList);// the div containing the slides
+
+var slideWidth = 144; // width of a slide including all margins, etc.
+var slidesAtOnce = 3; // no. of slides to appear at once (requires odd number to have a centered slide)
+
+// -- * no editing should be needed below * -- //
+
+var originPosition = {};
+var is_animating = 0;
+var currentStripPosition = 0;
+var centeringPoint = 0;
+var rightScrollLimit = 0;
+
+// makeSlideStrip()
+// - figures out how many slides there are
+// - determines the centering point of the slide strip
+function makeSlideStrip() {
+ var slideTotal = 0;
+ centeringPoint = Math.ceil(slidesAtOnce/2);
+ for (var x in slideCode) {
+ slideTotal++;
+ }
+ var i = 0;
+ for (var code in slideCode) {
+ if (i <= centeringPoint-1) {
+ originPosition[code] = 0;
+ } else {
+ if (i >= slideTotal-centeringPoint+1) {
+ originPosition[code] = (slideTotal-slidesAtOnce)*slideWidth;
+ } else {
+ originPosition[code] = (i-centeringPoint+1)*slideWidth;
+ }
+ }
+ i++;
+ }
+ rightScrollLimit = -1*(slideTotal-slidesAtOnce)*slideWidth;
+}
+
+// slides with acceleration
+function slide(goal, id, go_left, cp) {
+ var div = document.getElementById(id);
+ var animation = {};
+ animation.time = 0.5; // in seconds
+ animation.fps = 60;
+ animation.goal = goal;
+ origin = 0.0;
+ animation.origin = Math.abs(origin);
+ animation.frames = (animation.time * animation.fps) - 1.0;
+ var current_frame = 0;
+ var motions = Math.abs(animation.goal - animation.origin);
+ function animate() {
+ var ease_right = function (t) { return (1 - Math.cos(t * Math.PI))/2.0; };
+ var ease = ease_right;
+ if (go_left == 1) {
+ ease = function(t) { return 1.0 - ease_right(t); };
+ }
+ var left = (ease(current_frame/animation.frames) * Math.abs(animation.goal - animation.origin)) - cp;
+ if(left < 0) {
+ left = 0;
+ }
+ if(!isNaN(left)) {
+ div.style.left = '-' + Math.round(left) + 'px';
+ }
+ current_frame += 1;
+ if (current_frame == animation.frames) {
+ is_animating = 0;
+ window.clearInterval(timeoutId)
+ }
+ }
+ var timeoutId = window.setInterval(animate, animation.time/animation.fps * 1000);
+}
+
+//Get style property
+function getStyle(element, cssProperty){
+ var elem = document.getElementById(element);
+ if(elem.currentStyle){
+ return elem.currentStyle[cssProperty]; //IE
+ } else{
+ var style = document.defaultView.getComputedStyle(elem, null); //firefox, Opera
+ return style.getPropertyValue(cssProperty);
+ }
+}
+
+// Left and right arrows
+function page_left() {
+ var amount = slideWidth;
+ animateSlide(amount, 'left');
+}
+
+function page_right() {
+ var amount = slideWidth;
+ animateSlide(amount, 'right');
+}
+
+
+// animates the strip
+// - sets arrows to on or off
+function animateSlide(amount,dir) {
+ var currentStripPosition = parseInt(getStyle(slideList,'left'));
+ var motionDistance;
+ if (amount == slideWidth ) {
+ motionDistance = slideWidth;
+ } else {
+ motionDistance = amount;
+ }
+
+ var rightarrow = document.getElementById(arrowRight);
+ var leftarrow = document.getElementById(arrowLeft);
+
+ function aToggle(state,aDir) {
+ if (state == 'on') {
+ if (aDir =='right') {
+ rightarrow.className = 'arrow-right-on';
+ rightarrow.href = "javascript:page_right()";
+ } else {
+ leftarrow.className = 'arrow-left-on';
+ leftarrow.href = "javascript:page_left()";
+ }
+ } else {
+ if (aDir =='right') {
+ rightarrow.href = "javascript:{}";
+ rightarrow.className = 'arrow-right-off';
+ } else {
+ leftarrow.href = "javascript:{}";
+ leftarrow.className = 'arrow-left-off';
+ }
+ }
+ }
+
+ function arrowChange(rP) {
+ if (rP >= rightScrollLimit) {
+ aToggle('on','right');
+ }
+ if (rP <= rightScrollLimit) {
+ aToggle('off','right');
+ }
+ if (rP <= slideWidth) {
+ aToggle('on','left');
+ }
+ if (rP >= 0) {
+ aToggle('off','left');
+ }
+ }
+
+ if (dir == 'right' && is_animating == 0) {
+ arrowChange(currentStripPosition-motionDistance);
+ is_animating = 1;
+ slide(motionDistance, slideList, 0, currentStripPosition);
+ } else if (dir == 'left' && is_animating == 0) {
+ arrowChange(currentStripPosition+motionDistance);
+ is_animating = 1;
+ rightStripPosition = currentStripPosition + motionDistance;
+ slide(motionDistance, slideList, 1, rightStripPosition);
+ }
+}
+
+function centerSlide(slideName) {
+ var currentStripPosition = parseInt(getStyle(slideList,'left'));
+ var dir = 'left';
+ var originpoint = Math.abs(currentStripPosition);
+ if (originpoint <= originPosition[slideName]) {
+ dir = 'right';
+ }
+ var motionValue = Math.abs(originPosition[slideName]-originpoint);
+ animateSlide(motionValue,dir);
+}
+
+
+function initCarousel(def) {
+ buildCarousel();
+ showPreview(def);
+ makeSlideStrip();
+}
diff --git a/tools/droiddoc/templates/assets/images/android-developers-logo.png b/tools/droiddoc/templates/assets/images/android-developers-logo.png
new file mode 100644
index 000000000..30a8f627c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/android-developers-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/android_wrench.png b/tools/droiddoc/templates/assets/images/android_wrench.png
new file mode 100644
index 000000000..6390a2d80
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/android_wrench.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_left_off.jpg b/tools/droiddoc/templates/assets/images/arrow_left_off.jpg
new file mode 100755
index 000000000..fd32a64f1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_left_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_left_on.jpg b/tools/droiddoc/templates/assets/images/arrow_left_on.jpg
new file mode 100755
index 000000000..143184b8c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_left_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_right_off.jpg b/tools/droiddoc/templates/assets/images/arrow_right_off.jpg
new file mode 100755
index 000000000..17d2efee2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_right_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/arrow_right_on.jpg b/tools/droiddoc/templates/assets/images/arrow_right_on.jpg
new file mode 100755
index 000000000..baa2af12c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/arrow_right_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg b/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg
new file mode 100755
index 000000000..a6d6f0e9a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_community_leftDiv.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_fade.jpg b/tools/droiddoc/templates/assets/images/bg_fade.jpg
new file mode 100755
index 000000000..c6c70b6f1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_fade.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_images_sprite.png b/tools/droiddoc/templates/assets/images/bg_images_sprite.png
new file mode 100755
index 000000000..84437e799
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_images_sprite.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/bg_logo.png b/tools/droiddoc/templates/assets/images/bg_logo.png
new file mode 100755
index 000000000..8c57fc48f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/bg_logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/body-gradient-tab.png b/tools/droiddoc/templates/assets/images/body-gradient-tab.png
new file mode 100644
index 000000000..5223ac3a2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/body-gradient-tab.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/body-gradient.png b/tools/droiddoc/templates/assets/images/body-gradient.png
new file mode 100755
index 000000000..9d5985552
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/body-gradient.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/developers-logo.png b/tools/droiddoc/templates/assets/images/developers-logo.png
new file mode 100755
index 000000000..08122ee12
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/developers-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/grad-rule-qv.png b/tools/droiddoc/templates/assets/images/grad-rule-qv.png
new file mode 100644
index 000000000..bae2d18c2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/grad-rule-qv.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/IO-logo.png b/tools/droiddoc/templates/assets/images/home/IO-logo.png
new file mode 100644
index 000000000..65334c81a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/IO-logo.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg b/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg
new file mode 100755
index 000000000..dacd40181
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_bottom.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/bg_home_middle.png b/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
new file mode 100644
index 000000000..4221e96fc
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/bg_home_middle.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png b/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png
new file mode 100755
index 000000000..e98c94280
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/carousel_buttons_sprite.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/devphone-large.png b/tools/droiddoc/templates/assets/images/home/devphone-large.png
new file mode 100755
index 000000000..cbf94c81a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/devphone-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/devphone-small.png b/tools/droiddoc/templates/assets/images/home/devphone-small.png
new file mode 100755
index 000000000..b8487f57a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/devphone-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/maps-large.png b/tools/droiddoc/templates/assets/images/home/maps-large.png
new file mode 100644
index 000000000..b26f65a59
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/maps-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/maps-small.png b/tools/droiddoc/templates/assets/images/home/maps-small.png
new file mode 100644
index 000000000..cc5f1fa02
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/maps-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/market-large.png b/tools/droiddoc/templates/assets/images/home/market-large.png
new file mode 100644
index 000000000..069fee712
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/market-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/market-small.png b/tools/droiddoc/templates/assets/images/home/market-small.png
new file mode 100644
index 000000000..fa1201c43
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/market-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/sdk-large.png b/tools/droiddoc/templates/assets/images/home/sdk-large.png
new file mode 100644
index 000000000..315a1bfc8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/sdk-large.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/home/sdk-small.png b/tools/droiddoc/templates/assets/images/home/sdk-small.png
new file mode 100644
index 000000000..0f1670d7f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/home/sdk-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/hr_gray_main.jpg b/tools/droiddoc/templates/assets/images/hr_gray_main.jpg
new file mode 100755
index 000000000..f7a0a2f92
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/hr_gray_main.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/hr_gray_side.jpg b/tools/droiddoc/templates/assets/images/hr_gray_side.jpg
new file mode 100755
index 000000000..666747669
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/hr_gray_side.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_contribute.jpg b/tools/droiddoc/templates/assets/images/icon_contribute.jpg
new file mode 100755
index 000000000..1aa12b6a1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_contribute.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_download.jpg b/tools/droiddoc/templates/assets/images/icon_download.jpg
new file mode 100755
index 000000000..f8c11658b
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_download.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_download2.jpg b/tools/droiddoc/templates/assets/images/icon_download2.jpg
new file mode 100755
index 000000000..c0af7a28f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_download2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_market.jpg b/tools/droiddoc/templates/assets/images/icon_market.jpg
new file mode 100755
index 000000000..225e7270b
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_market.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_robot.jpg b/tools/droiddoc/templates/assets/images/icon_robot.jpg
new file mode 100755
index 000000000..ca0fd3933
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_robot.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/icon_world.jpg b/tools/droiddoc/templates/assets/images/icon_world.jpg
new file mode 100755
index 000000000..65b8fa615
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/icon_world.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/left_off.jpg b/tools/droiddoc/templates/assets/images/left_off.jpg
new file mode 100755
index 000000000..fd32a64f1
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/left_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/left_on.jpg b/tools/droiddoc/templates/assets/images/left_on.jpg
new file mode 100755
index 000000000..143184b8c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/left_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg b/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg
new file mode 100755
index 000000000..e743f860f
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/logo_breadcrumbz.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/preliminary.png b/tools/droiddoc/templates/assets/images/preliminary.png
new file mode 100755
index 000000000..aad1644e8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/preliminary.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-e.gif b/tools/droiddoc/templates/assets/images/resizable-e.gif
new file mode 100755
index 000000000..f748097f4
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-e.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-e2.gif b/tools/droiddoc/templates/assets/images/resizable-e2.gif
new file mode 100755
index 000000000..e45d0c5be
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-e2.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-eg.gif b/tools/droiddoc/templates/assets/images/resizable-eg.gif
new file mode 100755
index 000000000..619661626
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-eg.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-s.gif b/tools/droiddoc/templates/assets/images/resizable-s.gif
new file mode 100755
index 000000000..7f6a4eb0e
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-s.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-s2.gif b/tools/droiddoc/templates/assets/images/resizable-s2.gif
new file mode 100755
index 000000000..99e869c8a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-s2.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/resizable-sg.gif b/tools/droiddoc/templates/assets/images/resizable-sg.gif
new file mode 100755
index 000000000..b4bea1014
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/resizable-sg.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/right_off.jpg b/tools/droiddoc/templates/assets/images/right_off.jpg
new file mode 100755
index 000000000..17d2efee2
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/right_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/right_on.jpg b/tools/droiddoc/templates/assets/images/right_on.jpg
new file mode 100755
index 000000000..baa2af12c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/right_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/sidenav-rule.png b/tools/droiddoc/templates/assets/images/sidenav-rule.png
new file mode 100644
index 000000000..eab992063
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/sidenav-rule.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_1.jpg b/tools/droiddoc/templates/assets/images/slide_1.jpg
new file mode 100755
index 000000000..6d75be1df
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_1.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_2.jpg b/tools/droiddoc/templates/assets/images/slide_2.jpg
new file mode 100755
index 000000000..aa994c2ef
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_3.jpg b/tools/droiddoc/templates/assets/images/slide_3.jpg
new file mode 100755
index 000000000..b04deb360
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_3.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_1.jpg b/tools/droiddoc/templates/assets/images/slide_large_1.jpg
new file mode 100755
index 000000000..a992e923d
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_1.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_2.jpg b/tools/droiddoc/templates/assets/images/slide_large_2.jpg
new file mode 100755
index 000000000..9af63f40c
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_2.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_large_3.jpg b/tools/droiddoc/templates/assets/images/slide_large_3.jpg
new file mode 100755
index 000000000..fcf236cab
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_large_3.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_off.jpg b/tools/droiddoc/templates/assets/images/slide_off.jpg
new file mode 100755
index 000000000..597122748
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_off.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/slide_on.jpg b/tools/droiddoc/templates/assets/images/slide_on.jpg
new file mode 100755
index 000000000..7ca35773b
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/slide_on.jpg
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/spacer.gif b/tools/droiddoc/templates/assets/images/spacer.gif
new file mode 100755
index 000000000..f96b355f4
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/spacer.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-closed-small.png b/tools/droiddoc/templates/assets/images/triangle-closed-small.png
new file mode 100644
index 000000000..002364a6a
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-closed-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-closed.png b/tools/droiddoc/templates/assets/images/triangle-closed.png
new file mode 100644
index 000000000..a34a05580
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-closed.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-opened-small.png b/tools/droiddoc/templates/assets/images/triangle-opened-small.png
new file mode 100644
index 000000000..e1eb784ff
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-opened-small.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/triangle-opened.png b/tools/droiddoc/templates/assets/images/triangle-opened.png
new file mode 100644
index 000000000..a70960485
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/triangle-opened.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/images/video-droid.png b/tools/droiddoc/templates/assets/images/video-droid.png
new file mode 100644
index 000000000..25163b619
--- /dev/null
+++ b/tools/droiddoc/templates/assets/images/video-droid.png
Binary files differ
diff --git a/tools/droiddoc/templates/assets/jdiff_logo.gif b/tools/droiddoc/templates/assets/jdiff_logo.gif
new file mode 100644
index 000000000..69700870e
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jdiff_logo.gif
Binary files differ
diff --git a/tools/droiddoc/templates/assets/jquery-history.js b/tools/droiddoc/templates/assets/jquery-history.js
new file mode 100644
index 000000000..ef96ec39d
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jquery-history.js
@@ -0,0 +1,78 @@
+/**
+ * jQuery history event v0.1
+ * Copyright (c) 2008 Tom Rodenberg <tarodenberg gmail com>
+ * Licensed under the GPL (http://www.gnu.org/licenses/gpl.html) license.
+ */
+(function($) {
+ var currentHash, previousNav, timer, hashTrim = /^.*#/;
+
+ var msie = {
+ iframe: null,
+ getDoc: function() {
+ return msie.iframe.contentWindow.document;
+ },
+ getHash: function() {
+ return msie.getDoc().location.hash;
+ },
+ setHash: function(hash) {
+ var d = msie.getDoc();
+ d.open();
+ d.close();
+ d.location.hash = hash;
+ }
+ };
+
+ var historycheck = function() {
+ var hash = msie.iframe ? msie.getHash() : location.hash;
+ if (hash != currentHash) {
+ currentHash = hash;
+ if (msie.iframe) {
+ location.hash = currentHash;
+ }
+ var current = $.history.getCurrent();
+ $.event.trigger('history', [current, previousNav]);
+ previousNav = current;
+ }
+ };
+
+ $.history = {
+ add: function(hash) {
+ hash = '#' + hash.replace(hashTrim, '');
+ if (currentHash != hash) {
+ var previous = $.history.getCurrent();
+ location.hash = currentHash = hash;
+ if (msie.iframe) {
+ msie.setHash(currentHash);
+ }
+ $.event.trigger('historyadd', [$.history.getCurrent(), previous]);
+ }
+ if (!timer) {
+ timer = setInterval(historycheck, 100);
+ }
+ },
+ getCurrent: function() {
+ if (currentHash) {
+ return currentHash.replace(hashTrim, '');
+ } else {
+ return "";
+ }
+ }
+ };
+
+ $.fn.history = function(fn) {
+ $(this).bind('history', fn);
+ };
+
+ $.fn.historyadd = function(fn) {
+ $(this).bind('historyadd', fn);
+ };
+
+ $(function() {
+ currentHash = location.hash;
+ if ($.browser.msie) {
+ msie.iframe = $('<iframe style="display:none" src="javascript:false;"></iframe>').prependTo('body')[0];
+ msie.setHash(currentHash);
+ currentHash = msie.getHash();
+ }
+ });
+})(jQuery);
diff --git a/tools/droiddoc/templates/assets/jquery-resizable.min.js b/tools/droiddoc/templates/assets/jquery-resizable.min.js
new file mode 100755
index 000000000..b3b4aedbc
--- /dev/null
+++ b/tools/droiddoc/templates/assets/jquery-resizable.min.js
@@ -0,0 +1,94 @@
+/*
+ * jQuery 1.2.6 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $
+ * $Rev: 5685 $
+ */
+/* Includes jQuery UI core and resizable */
+(function(){var _jQuery=window.jQuery,_$=window.$;var jQuery=window.jQuery=window.$=function(selector,context){return new jQuery.fn.init(selector,context);};var quickExpr=/^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/,isSimple=/^.[^:#\[\.]*$/,undefined;jQuery.fn=jQuery.prototype={init:function(selector,context){selector=selector||document;if(selector.nodeType){this[0]=selector;this.length=1;return this;}if(typeof selector=="string"){var match=quickExpr.exec(selector);if(match&&(match[1]||!context)){if(match[1])selector=jQuery.clean([match[1]],context);else{var elem=document.getElementById(match[3]);if(elem){if(elem.id!=match[3])return jQuery().find(selector);return jQuery(elem);}selector=[];}}else
+return jQuery(context).find(selector);}else if(jQuery.isFunction(selector))return jQuery(document)[jQuery.fn.ready?"ready":"load"](selector);return this.setArray(jQuery.makeArray(selector));},jquery:"1.2.6",size:function(){return this.length;},length:0,get:function(num){return num==undefined?jQuery.makeArray(this):this[num];},pushStack:function(elems){var ret=jQuery(elems);ret.prevObject=this;return ret;},setArray:function(elems){this.length=0;Array.prototype.push.apply(this,elems);return this;},each:function(callback,args){return jQuery.each(this,callback,args);},index:function(elem){var ret=-1;return jQuery.inArray(elem&&elem.jquery?elem[0]:elem,this);},attr:function(name,value,type){var options=name;if(name.constructor==String)if(value===undefined)return this[0]&&jQuery[type||"attr"](this[0],name);else{options={};options[name]=value;}return this.each(function(i){for(name in options)jQuery.attr(type?this.style:this,name,jQuery.prop(this,options[name],type,i,name));});},css:function(key,value){if((key=='width'||key=='height')&&parseFloat(value)<0)value=undefined;return this.attr(key,value,"curCSS");},text:function(text){if(typeof text!="object"&&text!=null)return this.empty().append((this[0]&&this[0].ownerDocument||document).createTextNode(text));var ret="";jQuery.each(text||this,function(){jQuery.each(this.childNodes,function(){if(this.nodeType!=8)ret+=this.nodeType!=1?this.nodeValue:jQuery.fn.text([this]);});});return ret;},wrapAll:function(html){if(this[0])jQuery(html,this[0].ownerDocument).clone().insertBefore(this[0]).map(function(){var elem=this;while(elem.firstChild)elem=elem.firstChild;return elem;}).append(this);return this;},wrapInner:function(html){return this.each(function(){jQuery(this).contents().wrapAll(html);});},wrap:function(html){return this.each(function(){jQuery(this).wrapAll(html);});},append:function(){return this.domManip(arguments,true,false,function(elem){if(this.nodeType==1)this.appendChild(elem);});},prepend:function(){return this.domManip(arguments,true,true,function(elem){if(this.nodeType==1)this.insertBefore(elem,this.firstChild);});},before:function(){return this.domManip(arguments,false,false,function(elem){this.parentNode.insertBefore(elem,this);});},after:function(){return this.domManip(arguments,false,true,function(elem){this.parentNode.insertBefore(elem,this.nextSibling);});},end:function(){return this.prevObject||jQuery([]);},find:function(selector){var elems=jQuery.map(this,function(elem){return jQuery.find(selector,elem);});return this.pushStack(/[^+>] [^+>]/.test(selector)||selector.indexOf("..")>-1?jQuery.unique(elems):elems);},clone:function(events){var ret=this.map(function(){if(jQuery.browser.msie&&!jQuery.isXMLDoc(this)){var clone=this.cloneNode(true),container=document.createElement("div");container.appendChild(clone);return jQuery.clean([container.innerHTML])[0];}else
+return this.cloneNode(true);});var clone=ret.find("*").andSelf().each(function(){if(this[expando]!=undefined)this[expando]=null;});if(events===true)this.find("*").andSelf().each(function(i){if(this.nodeType==3)return;var events=jQuery.data(this,"events");for(var type in events)for(var handler in events[type])jQuery.event.add(clone[i],type,events[type][handler],events[type][handler].data);});return ret;},filter:function(selector){return this.pushStack(jQuery.isFunction(selector)&&jQuery.grep(this,function(elem,i){return selector.call(elem,i);})||jQuery.multiFilter(selector,this));},not:function(selector){if(selector.constructor==String)if(isSimple.test(selector))return this.pushStack(jQuery.multiFilter(selector,this,true));else
+selector=jQuery.multiFilter(selector,this);var isArrayLike=selector.length&&selector[selector.length-1]!==undefined&&!selector.nodeType;return this.filter(function(){return isArrayLike?jQuery.inArray(this,selector)<0:this!=selector;});},add:function(selector){return this.pushStack(jQuery.unique(jQuery.merge(this.get(),typeof selector=='string'?jQuery(selector):jQuery.makeArray(selector))));},is:function(selector){return!!selector&&jQuery.multiFilter(selector,this).length>0;},hasClass:function(selector){return this.is("."+selector);},val:function(value){if(value==undefined){if(this.length){var elem=this[0];if(jQuery.nodeName(elem,"select")){var index=elem.selectedIndex,values=[],options=elem.options,one=elem.type=="select-one";if(index<0)return null;for(var i=one?index:0,max=one?index+1:options.length;i<max;i++){var option=options[i];if(option.selected){value=jQuery.browser.msie&&!option.attributes.value.specified?option.text:option.value;if(one)return value;values.push(value);}}return values;}else
+return(this[0].value||"").replace(/\r/g,"");}return undefined;}if(value.constructor==Number)value+='';return this.each(function(){if(this.nodeType!=1)return;if(value.constructor==Array&&/radio|checkbox/.test(this.type))this.checked=(jQuery.inArray(this.value,value)>=0||jQuery.inArray(this.name,value)>=0);else if(jQuery.nodeName(this,"select")){var values=jQuery.makeArray(value);jQuery("option",this).each(function(){this.selected=(jQuery.inArray(this.value,values)>=0||jQuery.inArray(this.text,values)>=0);});if(!values.length)this.selectedIndex=-1;}else
+this.value=value;});},html:function(value){return value==undefined?(this[0]?this[0].innerHTML:null):this.empty().append(value);},replaceWith:function(value){return this.after(value).remove();},eq:function(i){return this.slice(i,i+1);},slice:function(){return this.pushStack(Array.prototype.slice.apply(this,arguments));},map:function(callback){return this.pushStack(jQuery.map(this,function(elem,i){return callback.call(elem,i,elem);}));},andSelf:function(){return this.add(this.prevObject);},data:function(key,value){var parts=key.split(".");parts[1]=parts[1]?"."+parts[1]:"";if(value===undefined){var data=this.triggerHandler("getData"+parts[1]+"!",[parts[0]]);if(data===undefined&&this.length)data=jQuery.data(this[0],key);return data===undefined&&parts[1]?this.data(parts[0]):data;}else
+return this.trigger("setData"+parts[1]+"!",[parts[0],value]).each(function(){jQuery.data(this,key,value);});},removeData:function(key){return this.each(function(){jQuery.removeData(this,key);});},domManip:function(args,table,reverse,callback){var clone=this.length>1,elems;return this.each(function(){if(!elems){elems=jQuery.clean(args,this.ownerDocument);if(reverse)elems.reverse();}var obj=this;if(table&&jQuery.nodeName(this,"table")&&jQuery.nodeName(elems[0],"tr"))obj=this.getElementsByTagName("tbody")[0]||this.appendChild(this.ownerDocument.createElement("tbody"));var scripts=jQuery([]);jQuery.each(elems,function(){var elem=clone?jQuery(this).clone(true)[0]:this;if(jQuery.nodeName(elem,"script"))scripts=scripts.add(elem);else{if(elem.nodeType==1)scripts=scripts.add(jQuery("script",elem).remove());callback.call(obj,elem);}});scripts.each(evalScript);});}};jQuery.fn.init.prototype=jQuery.fn;function evalScript(i,elem){if(elem.src)jQuery.ajax({url:elem.src,async:false,dataType:"script"});else
+jQuery.globalEval(elem.text||elem.textContent||elem.innerHTML||"");if(elem.parentNode)elem.parentNode.removeChild(elem);}function now(){return+new Date;}jQuery.extend=jQuery.fn.extend=function(){var target=arguments[0]||{},i=1,length=arguments.length,deep=false,options;if(target.constructor==Boolean){deep=target;target=arguments[1]||{};i=2;}if(typeof target!="object"&&typeof target!="function")target={};if(length==i){target=this;--i;}for(;i<length;i++)if((options=arguments[i])!=null)for(var name in options){var src=target[name],copy=options[name];if(target===copy)continue;if(deep&&copy&&typeof copy=="object"&&!copy.nodeType)target[name]=jQuery.extend(deep,src||(copy.length!=null?[]:{}),copy);else if(copy!==undefined)target[name]=copy;}return target;};var expando="jQuery"+now(),uuid=0,windowData={},exclude=/z-?index|font-?weight|opacity|zoom|line-?height/i,defaultView=document.defaultView||{};jQuery.extend({noConflict:function(deep){window.$=_$;if(deep)window.jQuery=_jQuery;return jQuery;},isFunction:function(fn){return!!fn&&typeof fn!="string"&&!fn.nodeName&&fn.constructor!=Array&&/^[\s[]?function/.test(fn+"");},isXMLDoc:function(elem){return elem.documentElement&&!elem.body||elem.tagName&&elem.ownerDocument&&!elem.ownerDocument.body;},globalEval:function(data){data=jQuery.trim(data);if(data){var head=document.getElementsByTagName("head")[0]||document.documentElement,script=document.createElement("script");script.type="text/javascript";if(jQuery.browser.msie)script.text=data;else
+script.appendChild(document.createTextNode(data));head.insertBefore(script,head.firstChild);head.removeChild(script);}},nodeName:function(elem,name){return elem.nodeName&&elem.nodeName.toUpperCase()==name.toUpperCase();},cache:{},data:function(elem,name,data){elem=elem==window?windowData:elem;var id=elem[expando];if(!id)id=elem[expando]=++uuid;if(name&&!jQuery.cache[id])jQuery.cache[id]={};if(data!==undefined)jQuery.cache[id][name]=data;return name?jQuery.cache[id][name]:id;},removeData:function(elem,name){elem=elem==window?windowData:elem;var id=elem[expando];if(name){if(jQuery.cache[id]){delete jQuery.cache[id][name];name="";for(name in jQuery.cache[id])break;if(!name)jQuery.removeData(elem);}}else{try{delete elem[expando];}catch(e){if(elem.removeAttribute)elem.removeAttribute(expando);}delete jQuery.cache[id];}},each:function(object,callback,args){var name,i=0,length=object.length;if(args){if(length==undefined){for(name in object)if(callback.apply(object[name],args)===false)break;}else
+for(;i<length;)if(callback.apply(object[i++],args)===false)break;}else{if(length==undefined){for(name in object)if(callback.call(object[name],name,object[name])===false)break;}else
+for(var value=object[0];i<length&&callback.call(value,i,value)!==false;value=object[++i]){}}return object;},prop:function(elem,value,type,i,name){if(jQuery.isFunction(value))value=value.call(elem,i);return value&&value.constructor==Number&&type=="curCSS"&&!exclude.test(name)?value+"px":value;},className:{add:function(elem,classNames){jQuery.each((classNames||"").split(/\s+/),function(i,className){if(elem.nodeType==1&&!jQuery.className.has(elem.className,className))elem.className+=(elem.className?" ":"")+className;});},remove:function(elem,classNames){if(elem.nodeType==1)elem.className=classNames!=undefined?jQuery.grep(elem.className.split(/\s+/),function(className){return!jQuery.className.has(classNames,className);}).join(" "):"";},has:function(elem,className){return jQuery.inArray(className,(elem.className||elem).toString().split(/\s+/))>-1;}},swap:function(elem,options,callback){var old={};for(var name in options){old[name]=elem.style[name];elem.style[name]=options[name];}callback.call(elem);for(var name in options)elem.style[name]=old[name];},css:function(elem,name,force){if(name=="width"||name=="height"){var val,props={position:"absolute",visibility:"hidden",display:"block"},which=name=="width"?["Left","Right"]:["Top","Bottom"];function getWH(){val=name=="width"?elem.offsetWidth:elem.offsetHeight;var padding=0,border=0;jQuery.each(which,function(){padding+=parseFloat(jQuery.curCSS(elem,"padding"+this,true))||0;border+=parseFloat(jQuery.curCSS(elem,"border"+this+"Width",true))||0;});val-=Math.round(padding+border);}if(jQuery(elem).is(":visible"))getWH();else
+jQuery.swap(elem,props,getWH);return Math.max(0,val);}return jQuery.curCSS(elem,name,force);},curCSS:function(elem,name,force){var ret,style=elem.style;function color(elem){if(!jQuery.browser.safari)return false;var ret=defaultView.getComputedStyle(elem,null);return!ret||ret.getPropertyValue("color")=="";}if(name=="opacity"&&jQuery.browser.msie){ret=jQuery.attr(style,"opacity");return ret==""?"1":ret;}if(jQuery.browser.opera&&name=="display"){var save=style.outline;style.outline="0 solid black";style.outline=save;}if(name.match(/float/i))name=styleFloat;if(!force&&style&&style[name])ret=style[name];else if(defaultView.getComputedStyle){if(name.match(/float/i))name="float";name=name.replace(/([A-Z])/g,"-$1").toLowerCase();var computedStyle=defaultView.getComputedStyle(elem,null);if(computedStyle&&!color(elem))ret=computedStyle.getPropertyValue(name);else{var swap=[],stack=[],a=elem,i=0;for(;a&&color(a);a=a.parentNode)stack.unshift(a);for(;i<stack.length;i++)if(color(stack[i])){swap[i]=stack[i].style.display;stack[i].style.display="block";}ret=name=="display"&&swap[stack.length-1]!=null?"none":(computedStyle&&computedStyle.getPropertyValue(name))||"";for(i=0;i<swap.length;i++)if(swap[i]!=null)stack[i].style.display=swap[i];}if(name=="opacity"&&ret=="")ret="1";}else if(elem.currentStyle){var camelCase=name.replace(/\-(\w)/g,function(all,letter){return letter.toUpperCase();});ret=elem.currentStyle[name]||elem.currentStyle[camelCase];if(!/^\d+(px)?$/i.test(ret)&&/^\d/.test(ret)){var left=style.left,rsLeft=elem.runtimeStyle.left;elem.runtimeStyle.left=elem.currentStyle.left;style.left=ret||0;ret=style.pixelLeft+"px";style.left=left;elem.runtimeStyle.left=rsLeft;}}return ret;},clean:function(elems,context){var ret=[];context=context||document;if(typeof context.createElement=='undefined')context=context.ownerDocument||context[0]&&context[0].ownerDocument||document;jQuery.each(elems,function(i,elem){if(!elem)return;if(elem.constructor==Number)elem+='';if(typeof elem=="string"){elem=elem.replace(/(<(\w+)[^>]*?)\/>/g,function(all,front,tag){return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i)?all:front+"></"+tag+">";});var tags=jQuery.trim(elem).toLowerCase(),div=context.createElement("div");var wrap=!tags.indexOf("<opt")&&[1,"<select multiple='multiple'>","</select>"]||!tags.indexOf("<leg")&&[1,"<fieldset>","</fieldset>"]||tags.match(/^<(thead|tbody|tfoot|colg|cap)/)&&[1,"<table>","</table>"]||!tags.indexOf("<tr")&&[2,"<table><tbody>","</tbody></table>"]||(!tags.indexOf("<td")||!tags.indexOf("<th"))&&[3,"<table><tbody><tr>","</tr></tbody></table>"]||!tags.indexOf("<col")&&[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"]||jQuery.browser.msie&&[1,"div<div>","</div>"]||[0,"",""];div.innerHTML=wrap[1]+elem+wrap[2];while(wrap[0]--)div=div.lastChild;if(jQuery.browser.msie){var tbody=!tags.indexOf("<table")&&tags.indexOf("<tbody")<0?div.firstChild&&div.firstChild.childNodes:wrap[1]=="<table>"&&tags.indexOf("<tbody")<0?div.childNodes:[];for(var j=tbody.length-1;j>=0;--j)if(jQuery.nodeName(tbody[j],"tbody")&&!tbody[j].childNodes.length)tbody[j].parentNode.removeChild(tbody[j]);if(/^\s/.test(elem))div.insertBefore(context.createTextNode(elem.match(/^\s*/)[0]),div.firstChild);}elem=jQuery.makeArray(div.childNodes);}if(elem.length===0&&(!jQuery.nodeName(elem,"form")&&!jQuery.nodeName(elem,"select")))return;if(elem[0]==undefined||jQuery.nodeName(elem,"form")||elem.options)ret.push(elem);else
+ret=jQuery.merge(ret,elem);});return ret;},attr:function(elem,name,value){if(!elem||elem.nodeType==3||elem.nodeType==8)return undefined;var notxml=!jQuery.isXMLDoc(elem),set=value!==undefined,msie=jQuery.browser.msie;name=notxml&&jQuery.props[name]||name;if(elem.tagName){var special=/href|src|style/.test(name);if(name=="selected"&&jQuery.browser.safari)elem.parentNode.selectedIndex;if(name in elem&&notxml&&!special){if(set){if(name=="type"&&jQuery.nodeName(elem,"input")&&elem.parentNode)throw"type property can't be changed";elem[name]=value;}if(jQuery.nodeName(elem,"form")&&elem.getAttributeNode(name))return elem.getAttributeNode(name).nodeValue;return elem[name];}if(msie&&notxml&&name=="style")return jQuery.attr(elem.style,"cssText",value);if(set)elem.setAttribute(name,""+value);var attr=msie&&notxml&&special?elem.getAttribute(name,2):elem.getAttribute(name);return attr===null?undefined:attr;}if(msie&&name=="opacity"){if(set){elem.zoom=1;elem.filter=(elem.filter||"").replace(/alpha\([^)]*\)/,"")+(parseInt(value)+''=="NaN"?"":"alpha(opacity="+value*100+")");}return elem.filter&&elem.filter.indexOf("opacity=")>=0?(parseFloat(elem.filter.match(/opacity=([^)]*)/)[1])/100)+'':"";}name=name.replace(/-([a-z])/ig,function(all,letter){return letter.toUpperCase();});if(set)elem[name]=value;return elem[name];},trim:function(text){return(text||"").replace(/^\s+|\s+$/g,"");},makeArray:function(array){var ret=[];if(array!=null){var i=array.length;if(i==null||array.split||array.setInterval||array.call)ret[0]=array;else
+while(i)ret[--i]=array[i];}return ret;},inArray:function(elem,array){for(var i=0,length=array.length;i<length;i++)if(array[i]===elem)return i;return-1;},merge:function(first,second){var i=0,elem,pos=first.length;if(jQuery.browser.msie){while(elem=second[i++])if(elem.nodeType!=8)first[pos++]=elem;}else
+while(elem=second[i++])first[pos++]=elem;return first;},unique:function(array){var ret=[],done={};try{for(var i=0,length=array.length;i<length;i++){var id=jQuery.data(array[i]);if(!done[id]){done[id]=true;ret.push(array[i]);}}}catch(e){ret=array;}return ret;},grep:function(elems,callback,inv){var ret=[];for(var i=0,length=elems.length;i<length;i++)if(!inv!=!callback(elems[i],i))ret.push(elems[i]);return ret;},map:function(elems,callback){var ret=[];for(var i=0,length=elems.length;i<length;i++){var value=callback(elems[i],i);if(value!=null)ret[ret.length]=value;}return ret.concat.apply([],ret);}});var userAgent=navigator.userAgent.toLowerCase();jQuery.browser={version:(userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/)||[])[1],safari:/webkit/.test(userAgent),opera:/opera/.test(userAgent),msie:/msie/.test(userAgent)&&!/opera/.test(userAgent),mozilla:/mozilla/.test(userAgent)&&!/(compatible|webkit)/.test(userAgent)};var styleFloat=jQuery.browser.msie?"styleFloat":"cssFloat";jQuery.extend({boxModel:!jQuery.browser.msie||document.compatMode=="CSS1Compat",props:{"for":"htmlFor","class":"className","float":styleFloat,cssFloat:styleFloat,styleFloat:styleFloat,readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing"}});jQuery.each({parent:function(elem){return elem.parentNode;},parents:function(elem){return jQuery.dir(elem,"parentNode");},next:function(elem){return jQuery.nth(elem,2,"nextSibling");},prev:function(elem){return jQuery.nth(elem,2,"previousSibling");},nextAll:function(elem){return jQuery.dir(elem,"nextSibling");},prevAll:function(elem){return jQuery.dir(elem,"previousSibling");},siblings:function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},children:function(elem){return jQuery.sibling(elem.firstChild);},contents:function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}},function(name,fn){jQuery.fn[name]=function(selector){var ret=jQuery.map(this,fn);if(selector&&typeof selector=="string")ret=jQuery.multiFilter(selector,ret);return this.pushStack(jQuery.unique(ret));};});jQuery.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(name,original){jQuery.fn[name]=function(){var args=arguments;return this.each(function(){for(var i=0,length=args.length;i<length;i++)jQuery(args[i])[original](this);});};});jQuery.each({removeAttr:function(name){jQuery.attr(this,name,"");if(this.nodeType==1)this.removeAttribute(name);},addClass:function(classNames){jQuery.className.add(this,classNames);},removeClass:function(classNames){jQuery.className.remove(this,classNames);},toggleClass:function(classNames){jQuery.className[jQuery.className.has(this,classNames)?"remove":"add"](this,classNames);},remove:function(selector){if(!selector||jQuery.filter(selector,[this]).r.length){jQuery("*",this).add(this).each(function(){jQuery.event.remove(this);jQuery.removeData(this);});if(this.parentNode)this.parentNode.removeChild(this);}},empty:function(){jQuery(">*",this).remove();while(this.firstChild)this.removeChild(this.firstChild);}},function(name,fn){jQuery.fn[name]=function(){return this.each(fn,arguments);};});jQuery.each(["Height","Width"],function(i,name){var type=name.toLowerCase();jQuery.fn[type]=function(size){return this[0]==window?jQuery.browser.opera&&document.body["client"+name]||jQuery.browser.safari&&window["inner"+name]||document.compatMode=="CSS1Compat"&&document.documentElement["client"+name]||document.body["client"+name]:this[0]==document?Math.max(Math.max(document.body["scroll"+name],document.documentElement["scroll"+name]),Math.max(document.body["offset"+name],document.documentElement["offset"+name])):size==undefined?(this.length?jQuery.css(this[0],type):null):this.css(type,size.constructor==String?size:size+"px");};});function num(elem,prop){return elem[0]&&parseInt(jQuery.curCSS(elem[0],prop,true),10)||0;}var chars=jQuery.browser.safari&&parseInt(jQuery.browser.version)<417?"(?:[\\w*_-]|\\\\.)":"(?:[\\w\u0128-\uFFFF*_-]|\\\\.)",quickChild=new RegExp("^>\\s*("+chars+"+)"),quickID=new RegExp("^("+chars+"+)(#)("+chars+"+)"),quickClass=new RegExp("^([#.]?)("+chars+"*)");jQuery.extend({expr:{"":function(a,i,m){return m[2]=="*"||jQuery.nodeName(a,m[2]);},"#":function(a,i,m){return a.getAttribute("id")==m[2];},":":{lt:function(a,i,m){return i<m[3]-0;},gt:function(a,i,m){return i>m[3]-0;},nth:function(a,i,m){return m[3]-0==i;},eq:function(a,i,m){return m[3]-0==i;},first:function(a,i){return i==0;},last:function(a,i,m,r){return i==r.length-1;},even:function(a,i){return i%2==0;},odd:function(a,i){return i%2;},"first-child":function(a){return a.parentNode.getElementsByTagName("*")[0]==a;},"last-child":function(a){return jQuery.nth(a.parentNode.lastChild,1,"previousSibling")==a;},"only-child":function(a){return!jQuery.nth(a.parentNode.lastChild,2,"previousSibling");},parent:function(a){return a.firstChild;},empty:function(a){return!a.firstChild;},contains:function(a,i,m){return(a.textContent||a.innerText||jQuery(a).text()||"").indexOf(m[3])>=0;},visible:function(a){return"hidden"!=a.type&&jQuery.css(a,"display")!="none"&&jQuery.css(a,"visibility")!="hidden";},hidden:function(a){return"hidden"==a.type||jQuery.css(a,"display")=="none"||jQuery.css(a,"visibility")=="hidden";},enabled:function(a){return!a.disabled;},disabled:function(a){return a.disabled;},checked:function(a){return a.checked;},selected:function(a){return a.selected||jQuery.attr(a,"selected");},text:function(a){return"text"==a.type;},radio:function(a){return"radio"==a.type;},checkbox:function(a){return"checkbox"==a.type;},file:function(a){return"file"==a.type;},password:function(a){return"password"==a.type;},submit:function(a){return"submit"==a.type;},image:function(a){return"image"==a.type;},reset:function(a){return"reset"==a.type;},button:function(a){return"button"==a.type||jQuery.nodeName(a,"button");},input:function(a){return/input|select|textarea|button/i.test(a.nodeName);},has:function(a,i,m){return jQuery.find(m[3],a).length;},header:function(a){return/h\d/i.test(a.nodeName);},animated:function(a){return jQuery.grep(jQuery.timers,function(fn){return a==fn.elem;}).length;}}},parse:[/^(\[) *@?([\w-]+) *([!*$^~=]*) *('?"?)(.*?)\4 *\]/,/^(:)([\w-]+)\("?'?(.*?(\(.*?\))?[^(]*?)"?'?\)/,new RegExp("^([:.#]*)("+chars+"+)")],multiFilter:function(expr,elems,not){var old,cur=[];while(expr&&expr!=old){old=expr;var f=jQuery.filter(expr,elems,not);expr=f.t.replace(/^\s*,\s*/,"");cur=not?elems=f.r:jQuery.merge(cur,f.r);}return cur;},find:function(t,context){if(typeof t!="string")return[t];if(context&&context.nodeType!=1&&context.nodeType!=9)return[];context=context||document;var ret=[context],done=[],last,nodeName;while(t&&last!=t){var r=[];last=t;t=jQuery.trim(t);var foundToken=false,re=quickChild,m=re.exec(t);if(m){nodeName=m[1].toUpperCase();for(var i=0;ret[i];i++)for(var c=ret[i].firstChild;c;c=c.nextSibling)if(c.nodeType==1&&(nodeName=="*"||c.nodeName.toUpperCase()==nodeName))r.push(c);ret=r;t=t.replace(re,"");if(t.indexOf(" ")==0)continue;foundToken=true;}else{re=/^([>+~])\s*(\w*)/i;if((m=re.exec(t))!=null){r=[];var merge={};nodeName=m[2].toUpperCase();m=m[1];for(var j=0,rl=ret.length;j<rl;j++){var n=m=="~"||m=="+"?ret[j].nextSibling:ret[j].firstChild;for(;n;n=n.nextSibling)if(n.nodeType==1){var id=jQuery.data(n);if(m=="~"&&merge[id])break;if(!nodeName||n.nodeName.toUpperCase()==nodeName){if(m=="~")merge[id]=true;r.push(n);}if(m=="+")break;}}ret=r;t=jQuery.trim(t.replace(re,""));foundToken=true;}}if(t&&!foundToken){if(!t.indexOf(",")){if(context==ret[0])ret.shift();done=jQuery.merge(done,ret);r=ret=[context];t=" "+t.substr(1,t.length);}else{var re2=quickID;var m=re2.exec(t);if(m){m=[0,m[2],m[3],m[1]];}else{re2=quickClass;m=re2.exec(t);}m[2]=m[2].replace(/\\/g,"");var elem=ret[ret.length-1];if(m[1]=="#"&&elem&&elem.getElementById&&!jQuery.isXMLDoc(elem)){var oid=elem.getElementById(m[2]);if((jQuery.browser.msie||jQuery.browser.opera)&&oid&&typeof oid.id=="string"&&oid.id!=m[2])oid=jQuery('[@id="'+m[2]+'"]',elem)[0];ret=r=oid&&(!m[3]||jQuery.nodeName(oid,m[3]))?[oid]:[];}else{for(var i=0;ret[i];i++){var tag=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];if(tag=="*"&&ret[i].nodeName.toLowerCase()=="object")tag="param";r=jQuery.merge(r,ret[i].getElementsByTagName(tag));}if(m[1]==".")r=jQuery.classFilter(r,m[2]);if(m[1]=="#"){var tmp=[];for(var i=0;r[i];i++)if(r[i].getAttribute("id")==m[2]){tmp=[r[i]];break;}r=tmp;}ret=r;}t=t.replace(re2,"");}}if(t){var val=jQuery.filter(t,r);ret=r=val.r;t=jQuery.trim(val.t);}}if(t)ret=[];if(ret&&context==ret[0])ret.shift();done=jQuery.merge(done,ret);return done;},classFilter:function(r,m,not){m=" "+m+" ";var tmp=[];for(var i=0;r[i];i++){var pass=(" "+r[i].className+" ").indexOf(m)>=0;if(!not&&pass||not&&!pass)tmp.push(r[i]);}return tmp;},filter:function(t,r,not){var last;while(t&&t!=last){last=t;var p=jQuery.parse,m;for(var i=0;p[i];i++){m=p[i].exec(t);if(m){t=t.substring(m[0].length);m[2]=m[2].replace(/\\/g,"");break;}}if(!m)break;if(m[1]==":"&&m[2]=="not")r=isSimple.test(m[3])?jQuery.filter(m[3],r,true).r:jQuery(r).not(m[3]);else if(m[1]==".")r=jQuery.classFilter(r,m[2],not);else if(m[1]=="["){var tmp=[],type=m[3];for(var i=0,rl=r.length;i<rl;i++){var a=r[i],z=a[jQuery.props[m[2]]||m[2]];if(z==null||/href|src|selected/.test(m[2]))z=jQuery.attr(a,m[2])||'';if((type==""&&!!z||type=="="&&z==m[5]||type=="!="&&z!=m[5]||type=="^="&&z&&!z.indexOf(m[5])||type=="$="&&z.substr(z.length-m[5].length)==m[5]||(type=="*="||type=="~=")&&z.indexOf(m[5])>=0)^not)tmp.push(a);}r=tmp;}else if(m[1]==":"&&m[2]=="nth-child"){var merge={},tmp=[],test=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(m[3]=="even"&&"2n"||m[3]=="odd"&&"2n+1"||!/\D/.test(m[3])&&"0n+"+m[3]||m[3]),first=(test[1]+(test[2]||1))-0,last=test[3]-0;for(var i=0,rl=r.length;i<rl;i++){var node=r[i],parentNode=node.parentNode,id=jQuery.data(parentNode);if(!merge[id]){var c=1;for(var n=parentNode.firstChild;n;n=n.nextSibling)if(n.nodeType==1)n.nodeIndex=c++;merge[id]=true;}var add=false;if(first==0){if(node.nodeIndex==last)add=true;}else if((node.nodeIndex-last)%first==0&&(node.nodeIndex-last)/first>=0)add=true;if(add^not)tmp.push(node);}r=tmp;}else{var fn=jQuery.expr[m[1]];if(typeof fn=="object")fn=fn[m[2]];if(typeof fn=="string")fn=eval("false||function(a,i){return "+fn+";}");r=jQuery.grep(r,function(elem,i){return fn(elem,i,m,r);},not);}}return{r:r,t:t};},dir:function(elem,dir){var matched=[],cur=elem[dir];while(cur&&cur!=document){if(cur.nodeType==1)matched.push(cur);cur=cur[dir];}return matched;},nth:function(cur,result,dir,elem){result=result||1;var num=0;for(;cur;cur=cur[dir])if(cur.nodeType==1&&++num==result)break;return cur;},sibling:function(n,elem){var r=[];for(;n;n=n.nextSibling){if(n.nodeType==1&&n!=elem)r.push(n);}return r;}});jQuery.event={add:function(elem,types,handler,data){if(elem.nodeType==3||elem.nodeType==8)return;if(jQuery.browser.msie&&elem.setInterval)elem=window;if(!handler.guid)handler.guid=this.guid++;if(data!=undefined){var fn=handler;handler=this.proxy(fn,function(){return fn.apply(this,arguments);});handler.data=data;}var events=jQuery.data(elem,"events")||jQuery.data(elem,"events",{}),handle=jQuery.data(elem,"handle")||jQuery.data(elem,"handle",function(){if(typeof jQuery!="undefined"&&!jQuery.event.triggered)return jQuery.event.handle.apply(arguments.callee.elem,arguments);});handle.elem=elem;jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];handler.type=parts[1];var handlers=events[type];if(!handlers){handlers=events[type]={};if(!jQuery.event.special[type]||jQuery.event.special[type].setup.call(elem)===false){if(elem.addEventListener)elem.addEventListener(type,handle,false);else if(elem.attachEvent)elem.attachEvent("on"+type,handle);}}handlers[handler.guid]=handler;jQuery.event.global[type]=true;});elem=null;},guid:1,global:{},remove:function(elem,types,handler){if(elem.nodeType==3||elem.nodeType==8)return;var events=jQuery.data(elem,"events"),ret,index;if(events){if(types==undefined||(typeof types=="string"&&types.charAt(0)=="."))for(var type in events)this.remove(elem,type+(types||""));else{if(types.type){handler=types.handler;types=types.type;}jQuery.each(types.split(/\s+/),function(index,type){var parts=type.split(".");type=parts[0];if(events[type]){if(handler)delete events[type][handler.guid];else
+for(handler in events[type])if(!parts[1]||events[type][handler].type==parts[1])delete events[type][handler];for(ret in events[type])break;if(!ret){if(!jQuery.event.special[type]||jQuery.event.special[type].teardown.call(elem)===false){if(elem.removeEventListener)elem.removeEventListener(type,jQuery.data(elem,"handle"),false);else if(elem.detachEvent)elem.detachEvent("on"+type,jQuery.data(elem,"handle"));}ret=null;delete events[type];}}});}for(ret in events)break;if(!ret){var handle=jQuery.data(elem,"handle");if(handle)handle.elem=null;jQuery.removeData(elem,"events");jQuery.removeData(elem,"handle");}}},trigger:function(type,data,elem,donative,extra){data=jQuery.makeArray(data);if(type.indexOf("!")>=0){type=type.slice(0,-1);var exclusive=true;}if(!elem){if(this.global[type])jQuery("*").add([window,document]).trigger(type,data);}else{if(elem.nodeType==3||elem.nodeType==8)return undefined;var val,ret,fn=jQuery.isFunction(elem[type]||null),event=!data[0]||!data[0].preventDefault;if(event){data.unshift({type:type,target:elem,preventDefault:function(){},stopPropagation:function(){},timeStamp:now()});data[0][expando]=true;}data[0].type=type;if(exclusive)data[0].exclusive=true;var handle=jQuery.data(elem,"handle");if(handle)val=handle.apply(elem,data);if((!fn||(jQuery.nodeName(elem,'a')&&type=="click"))&&elem["on"+type]&&elem["on"+type].apply(elem,data)===false)val=false;if(event)data.shift();if(extra&&jQuery.isFunction(extra)){ret=extra.apply(elem,val==null?data:data.concat(val));if(ret!==undefined)val=ret;}if(fn&&donative!==false&&val!==false&&!(jQuery.nodeName(elem,'a')&&type=="click")){this.triggered=true;try{elem[type]();}catch(e){}}this.triggered=false;}return val;},handle:function(event){var val,ret,namespace,all,handlers;event=arguments[0]=jQuery.event.fix(event||window.event);namespace=event.type.split(".");event.type=namespace[0];namespace=namespace[1];all=!namespace&&!event.exclusive;handlers=(jQuery.data(this,"events")||{})[event.type];for(var j in handlers){var handler=handlers[j];if(all||handler.type==namespace){event.handler=handler;event.data=handler.data;ret=handler.apply(this,arguments);if(val!==false)val=ret;if(ret===false){event.preventDefault();event.stopPropagation();}}}return val;},fix:function(event){if(event[expando]==true)return event;var originalEvent=event;event={originalEvent:originalEvent};var props="altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode metaKey newValue originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target timeStamp toElement type view wheelDelta which".split(" ");for(var i=props.length;i;i--)event[props[i]]=originalEvent[props[i]];event[expando]=true;event.preventDefault=function(){if(originalEvent.preventDefault)originalEvent.preventDefault();originalEvent.returnValue=false;};event.stopPropagation=function(){if(originalEvent.stopPropagation)originalEvent.stopPropagation();originalEvent.cancelBubble=true;};event.timeStamp=event.timeStamp||now();if(!event.target)event.target=event.srcElement||document;if(event.target.nodeType==3)event.target=event.target.parentNode;if(!event.relatedTarget&&event.fromElement)event.relatedTarget=event.fromElement==event.target?event.toElement:event.fromElement;if(event.pageX==null&&event.clientX!=null){var doc=document.documentElement,body=document.body;event.pageX=event.clientX+(doc&&doc.scrollLeft||body&&body.scrollLeft||0)-(doc.clientLeft||0);event.pageY=event.clientY+(doc&&doc.scrollTop||body&&body.scrollTop||0)-(doc.clientTop||0);}if(!event.which&&((event.charCode||event.charCode===0)?event.charCode:event.keyCode))event.which=event.charCode||event.keyCode;if(!event.metaKey&&event.ctrlKey)event.metaKey=event.ctrlKey;if(!event.which&&event.button)event.which=(event.button&1?1:(event.button&2?3:(event.button&4?2:0)));return event;},proxy:function(fn,proxy){proxy.guid=fn.guid=fn.guid||proxy.guid||this.guid++;return proxy;},special:{ready:{setup:function(){bindReady();return;},teardown:function(){return;}},mouseenter:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseover",jQuery.event.special.mouseenter.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseover",jQuery.event.special.mouseenter.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseenter";return jQuery.event.handle.apply(this,arguments);}},mouseleave:{setup:function(){if(jQuery.browser.msie)return false;jQuery(this).bind("mouseout",jQuery.event.special.mouseleave.handler);return true;},teardown:function(){if(jQuery.browser.msie)return false;jQuery(this).unbind("mouseout",jQuery.event.special.mouseleave.handler);return true;},handler:function(event){if(withinElement(event,this))return true;event.type="mouseleave";return jQuery.event.handle.apply(this,arguments);}}}};jQuery.fn.extend({bind:function(type,data,fn){return type=="unload"?this.one(type,data,fn):this.each(function(){jQuery.event.add(this,type,fn||data,fn&&data);});},one:function(type,data,fn){var one=jQuery.event.proxy(fn||data,function(event){jQuery(this).unbind(event,one);return(fn||data).apply(this,arguments);});return this.each(function(){jQuery.event.add(this,type,one,fn&&data);});},unbind:function(type,fn){return this.each(function(){jQuery.event.remove(this,type,fn);});},trigger:function(type,data,fn){return this.each(function(){jQuery.event.trigger(type,data,this,true,fn);});},triggerHandler:function(type,data,fn){return this[0]&&jQuery.event.trigger(type,data,this[0],false,fn);},toggle:function(fn){var args=arguments,i=1;while(i<args.length)jQuery.event.proxy(fn,args[i++]);return this.click(jQuery.event.proxy(fn,function(event){this.lastToggle=(this.lastToggle||0)%i;event.preventDefault();return args[this.lastToggle++].apply(this,arguments)||false;}));},hover:function(fnOver,fnOut){return this.bind('mouseenter',fnOver).bind('mouseleave',fnOut);},ready:function(fn){bindReady();if(jQuery.isReady)fn.call(document,jQuery);else
+jQuery.readyList.push(function(){return fn.call(this,jQuery);});return this;}});jQuery.extend({isReady:false,readyList:[],ready:function(){if(!jQuery.isReady){jQuery.isReady=true;if(jQuery.readyList){jQuery.each(jQuery.readyList,function(){this.call(document);});jQuery.readyList=null;}jQuery(document).triggerHandler("ready");}}});var readyBound=false;function bindReady(){if(readyBound)return;readyBound=true;if(document.addEventListener&&!jQuery.browser.opera)document.addEventListener("DOMContentLoaded",jQuery.ready,false);if(jQuery.browser.msie&&window==top)(function(){if(jQuery.isReady)return;try{document.documentElement.doScroll("left");}catch(error){setTimeout(arguments.callee,0);return;}jQuery.ready();})();if(jQuery.browser.opera)document.addEventListener("DOMContentLoaded",function(){if(jQuery.isReady)return;for(var i=0;i<document.styleSheets.length;i++)if(document.styleSheets[i].disabled){setTimeout(arguments.callee,0);return;}jQuery.ready();},false);if(jQuery.browser.safari){var numStyles;(function(){if(jQuery.isReady)return;if(document.readyState!="loaded"&&document.readyState!="complete"){setTimeout(arguments.callee,0);return;}if(numStyles===undefined)numStyles=jQuery("style, link[rel=stylesheet]").length;if(document.styleSheets.length!=numStyles){setTimeout(arguments.callee,0);return;}jQuery.ready();})();}jQuery.event.add(window,"load",jQuery.ready);}jQuery.each(("blur,focus,load,resize,scroll,unload,click,dblclick,"+"mousedown,mouseup,mousemove,mouseover,mouseout,change,select,"+"submit,keydown,keypress,keyup,error").split(","),function(i,name){jQuery.fn[name]=function(fn){return fn?this.bind(name,fn):this.trigger(name);};});var withinElement=function(event,elem){var parent=event.relatedTarget;while(parent&&parent!=elem)try{parent=parent.parentNode;}catch(error){parent=elem;}return parent==elem;};jQuery(window).bind("unload",function(){jQuery("*").add(document).unbind();});jQuery.fn.extend({_load:jQuery.fn.load,load:function(url,params,callback){if(typeof url!='string')return this._load(url);var off=url.indexOf(" ");if(off>=0){var selector=url.slice(off,url.length);url=url.slice(0,off);}callback=callback||function(){};var type="GET";if(params)if(jQuery.isFunction(params)){callback=params;params=null;}else{params=jQuery.param(params);type="POST";}var self=this;jQuery.ajax({url:url,type:type,dataType:"html",data:params,complete:function(res,status){if(status=="success"||status=="notmodified")self.html(selector?jQuery("<div/>").append(res.responseText.replace(/<script(.|\s)*?\/script>/g,"")).find(selector):res.responseText);self.each(callback,[res.responseText,status,res]);}});return this;},serialize:function(){return jQuery.param(this.serializeArray());},serializeArray:function(){return this.map(function(){return jQuery.nodeName(this,"form")?jQuery.makeArray(this.elements):this;}).filter(function(){return this.name&&!this.disabled&&(this.checked||/select|textarea/i.test(this.nodeName)||/text|hidden|password/i.test(this.type));}).map(function(i,elem){var val=jQuery(this).val();return val==null?null:val.constructor==Array?jQuery.map(val,function(val,i){return{name:elem.name,value:val};}):{name:elem.name,value:val};}).get();}});jQuery.each("ajaxStart,ajaxStop,ajaxComplete,ajaxError,ajaxSuccess,ajaxSend".split(","),function(i,o){jQuery.fn[o]=function(f){return this.bind(o,f);};});var jsc=now();jQuery.extend({get:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data=null;}return jQuery.ajax({type:"GET",url:url,data:data,success:callback,dataType:type});},getScript:function(url,callback){return jQuery.get(url,null,callback,"script");},getJSON:function(url,data,callback){return jQuery.get(url,data,callback,"json");},post:function(url,data,callback,type){if(jQuery.isFunction(data)){callback=data;data={};}return jQuery.ajax({type:"POST",url:url,data:data,success:callback,dataType:type});},ajaxSetup:function(settings){jQuery.extend(jQuery.ajaxSettings,settings);},ajaxSettings:{url:location.href,global:true,type:"GET",timeout:0,contentType:"application/x-www-form-urlencoded",processData:true,async:true,data:null,username:null,password:null,accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},ajax:function(s){s=jQuery.extend(true,s,jQuery.extend(true,{},jQuery.ajaxSettings,s));var jsonp,jsre=/=\?(&|$)/g,status,data,type=s.type.toUpperCase();if(s.data&&s.processData&&typeof s.data!="string")s.data=jQuery.param(s.data);if(s.dataType=="jsonp"){if(type=="GET"){if(!s.url.match(jsre))s.url+=(s.url.match(/\?/)?"&":"?")+(s.jsonp||"callback")+"=?";}else if(!s.data||!s.data.match(jsre))s.data=(s.data?s.data+"&":"")+(s.jsonp||"callback")+"=?";s.dataType="json";}if(s.dataType=="json"&&(s.data&&s.data.match(jsre)||s.url.match(jsre))){jsonp="jsonp"+jsc++;if(s.data)s.data=(s.data+"").replace(jsre,"="+jsonp+"$1");s.url=s.url.replace(jsre,"="+jsonp+"$1");s.dataType="script";window[jsonp]=function(tmp){data=tmp;success();complete();window[jsonp]=undefined;try{delete window[jsonp];}catch(e){}if(head)head.removeChild(script);};}if(s.dataType=="script"&&s.cache==null)s.cache=false;if(s.cache===false&&type=="GET"){var ts=now();var ret=s.url.replace(/(\?|&)_=.*?(&|$)/,"$1_="+ts+"$2");s.url=ret+((ret==s.url)?(s.url.match(/\?/)?"&":"?")+"_="+ts:"");}if(s.data&&type=="GET"){s.url+=(s.url.match(/\?/)?"&":"?")+s.data;s.data=null;}if(s.global&&!jQuery.active++)jQuery.event.trigger("ajaxStart");var remote=/^(?:\w+:)?\/\/([^\/?#]+)/;if(s.dataType=="script"&&type=="GET"&&remote.test(s.url)&&remote.exec(s.url)[1]!=location.host){var head=document.getElementsByTagName("head")[0];var script=document.createElement("script");script.src=s.url;if(s.scriptCharset)script.charset=s.scriptCharset;if(!jsonp){var done=false;script.onload=script.onreadystatechange=function(){if(!done&&(!this.readyState||this.readyState=="loaded"||this.readyState=="complete")){done=true;success();complete();head.removeChild(script);}};}head.appendChild(script);return undefined;}var requestDone=false;var xhr=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();if(s.username)xhr.open(type,s.url,s.async,s.username,s.password);else
+xhr.open(type,s.url,s.async);try{if(s.data)xhr.setRequestHeader("Content-Type",s.contentType);if(s.ifModified)xhr.setRequestHeader("If-Modified-Since",jQuery.lastModified[s.url]||"Thu, 01 Jan 1970 00:00:00 GMT");xhr.setRequestHeader("X-Requested-With","XMLHttpRequest");xhr.setRequestHeader("Accept",s.dataType&&s.accepts[s.dataType]?s.accepts[s.dataType]+", */*":s.accepts._default);}catch(e){}if(s.beforeSend&&s.beforeSend(xhr,s)===false){s.global&&jQuery.active--;xhr.abort();return false;}if(s.global)jQuery.event.trigger("ajaxSend",[xhr,s]);var onreadystatechange=function(isTimeout){if(!requestDone&&xhr&&(xhr.readyState==4||isTimeout=="timeout")){requestDone=true;if(ival){clearInterval(ival);ival=null;}status=isTimeout=="timeout"&&"timeout"||!jQuery.httpSuccess(xhr)&&"error"||s.ifModified&&jQuery.httpNotModified(xhr,s.url)&&"notmodified"||"success";if(status=="success"){try{data=jQuery.httpData(xhr,s.dataType,s.dataFilter);}catch(e){status="parsererror";}}if(status=="success"){var modRes;try{modRes=xhr.getResponseHeader("Last-Modified");}catch(e){}if(s.ifModified&&modRes)jQuery.lastModified[s.url]=modRes;if(!jsonp)success();}else
+jQuery.handleError(s,xhr,status);complete();if(s.async)xhr=null;}};if(s.async){var ival=setInterval(onreadystatechange,13);if(s.timeout>0)setTimeout(function(){if(xhr){xhr.abort();if(!requestDone)onreadystatechange("timeout");}},s.timeout);}try{xhr.send(s.data);}catch(e){jQuery.handleError(s,xhr,null,e);}if(!s.async)onreadystatechange();function success(){if(s.success)s.success(data,status);if(s.global)jQuery.event.trigger("ajaxSuccess",[xhr,s]);}function complete(){if(s.complete)s.complete(xhr,status);if(s.global)jQuery.event.trigger("ajaxComplete",[xhr,s]);if(s.global&&!--jQuery.active)jQuery.event.trigger("ajaxStop");}return xhr;},handleError:function(s,xhr,status,e){if(s.error)s.error(xhr,status,e);if(s.global)jQuery.event.trigger("ajaxError",[xhr,s,e]);},active:0,httpSuccess:function(xhr){try{return!xhr.status&&location.protocol=="file:"||(xhr.status>=200&&xhr.status<300)||xhr.status==304||xhr.status==1223||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpNotModified:function(xhr,url){try{var xhrRes=xhr.getResponseHeader("Last-Modified");return xhr.status==304||xhrRes==jQuery.lastModified[url]||jQuery.browser.safari&&xhr.status==undefined;}catch(e){}return false;},httpData:function(xhr,type,filter){var ct=xhr.getResponseHeader("content-type"),xml=type=="xml"||!type&&ct&&ct.indexOf("xml")>=0,data=xml?xhr.responseXML:xhr.responseText;if(xml&&data.documentElement.tagName=="parsererror")throw"parsererror";if(filter)data=filter(data,type);if(type=="script")jQuery.globalEval(data);if(type=="json")data=eval("("+data+")");return data;},param:function(a){var s=[];if(a.constructor==Array||a.jquery)jQuery.each(a,function(){s.push(encodeURIComponent(this.name)+"="+encodeURIComponent(this.value));});else
+for(var j in a)if(a[j]&&a[j].constructor==Array)jQuery.each(a[j],function(){s.push(encodeURIComponent(j)+"="+encodeURIComponent(this));});else
+s.push(encodeURIComponent(j)+"="+encodeURIComponent(jQuery.isFunction(a[j])?a[j]():a[j]));return s.join("&").replace(/%20/g,"+");}});jQuery.fn.extend({show:function(speed,callback){return speed?this.animate({height:"show",width:"show",opacity:"show"},speed,callback):this.filter(":hidden").each(function(){this.style.display=this.oldblock||"";if(jQuery.css(this,"display")=="none"){var elem=jQuery("<"+this.tagName+" />").appendTo("body");this.style.display=elem.css("display");if(this.style.display=="none")this.style.display="block";elem.remove();}}).end();},hide:function(speed,callback){return speed?this.animate({height:"hide",width:"hide",opacity:"hide"},speed,callback):this.filter(":visible").each(function(){this.oldblock=this.oldblock||jQuery.css(this,"display");this.style.display="none";}).end();},_toggle:jQuery.fn.toggle,toggle:function(fn,fn2){return jQuery.isFunction(fn)&&jQuery.isFunction(fn2)?this._toggle.apply(this,arguments):fn?this.animate({height:"toggle",width:"toggle",opacity:"toggle"},fn,fn2):this.each(function(){jQuery(this)[jQuery(this).is(":hidden")?"show":"hide"]();});},slideDown:function(speed,callback){return this.animate({height:"show"},speed,callback);},slideUp:function(speed,callback){return this.animate({height:"hide"},speed,callback);},slideToggle:function(speed,callback){return this.animate({height:"toggle"},speed,callback);},fadeIn:function(speed,callback){return this.animate({opacity:"show"},speed,callback);},fadeOut:function(speed,callback){return this.animate({opacity:"hide"},speed,callback);},fadeTo:function(speed,to,callback){return this.animate({opacity:to},speed,callback);},animate:function(prop,speed,easing,callback){var optall=jQuery.speed(speed,easing,callback);return this[optall.queue===false?"each":"queue"](function(){if(this.nodeType!=1)return false;var opt=jQuery.extend({},optall),p,hidden=jQuery(this).is(":hidden"),self=this;for(p in prop){if(prop[p]=="hide"&&hidden||prop[p]=="show"&&!hidden)return opt.complete.call(this);if(p=="height"||p=="width"){opt.display=jQuery.css(this,"display");opt.overflow=this.style.overflow;}}if(opt.overflow!=null)this.style.overflow="hidden";opt.curAnim=jQuery.extend({},prop);jQuery.each(prop,function(name,val){var e=new jQuery.fx(self,opt,name);if(/toggle|show|hide/.test(val))e[val=="toggle"?hidden?"show":"hide":val](prop);else{var parts=val.toString().match(/^([+-]=)?([\d+-.]+)(.*)$/),start=e.cur(true)||0;if(parts){var end=parseFloat(parts[2]),unit=parts[3]||"px";if(unit!="px"){self.style[name]=(end||1)+unit;start=((end||1)/e.cur(true))*start;self.style[name]=start+unit;}if(parts[1])end=((parts[1]=="-="?-1:1)*end)+start;e.custom(start,end,unit);}else
+e.custom(start,val,"");}});return true;});},queue:function(type,fn){if(jQuery.isFunction(type)||(type&&type.constructor==Array)){fn=type;type="fx";}if(!type||(typeof type=="string"&&!fn))return queue(this[0],type);return this.each(function(){if(fn.constructor==Array)queue(this,type,fn);else{queue(this,type).push(fn);if(queue(this,type).length==1)fn.call(this);}});},stop:function(clearQueue,gotoEnd){var timers=jQuery.timers;if(clearQueue)this.queue([]);this.each(function(){for(var i=timers.length-1;i>=0;i--)if(timers[i].elem==this){if(gotoEnd)timers[i](true);timers.splice(i,1);}});if(!gotoEnd)this.dequeue();return this;}});var queue=function(elem,type,array){if(elem){type=type||"fx";var q=jQuery.data(elem,type+"queue");if(!q||array)q=jQuery.data(elem,type+"queue",jQuery.makeArray(array));}return q;};jQuery.fn.dequeue=function(type){type=type||"fx";return this.each(function(){var q=queue(this,type);q.shift();if(q.length)q[0].call(this);});};jQuery.extend({speed:function(speed,easing,fn){var opt=speed&&speed.constructor==Object?speed:{complete:fn||!fn&&easing||jQuery.isFunction(speed)&&speed,duration:speed,easing:fn&&easing||easing&&easing.constructor!=Function&&easing};opt.duration=(opt.duration&&opt.duration.constructor==Number?opt.duration:jQuery.fx.speeds[opt.duration])||jQuery.fx.speeds.def;opt.old=opt.complete;opt.complete=function(){if(opt.queue!==false)jQuery(this).dequeue();if(jQuery.isFunction(opt.old))opt.old.call(this);};return opt;},easing:{linear:function(p,n,firstNum,diff){return firstNum+diff*p;},swing:function(p,n,firstNum,diff){return((-Math.cos(p*Math.PI)/2)+0.5)*diff+firstNum;}},timers:[],timerId:null,fx:function(elem,options,prop){this.options=options;this.elem=elem;this.prop=prop;if(!options.orig)options.orig={};}});jQuery.fx.prototype={update:function(){if(this.options.step)this.options.step.call(this.elem,this.now,this);(jQuery.fx.step[this.prop]||jQuery.fx.step._default)(this);if(this.prop=="height"||this.prop=="width")this.elem.style.display="block";},cur:function(force){if(this.elem[this.prop]!=null&&this.elem.style[this.prop]==null)return this.elem[this.prop];var r=parseFloat(jQuery.css(this.elem,this.prop,force));return r&&r>-10000?r:parseFloat(jQuery.curCSS(this.elem,this.prop))||0;},custom:function(from,to,unit){this.startTime=now();this.start=from;this.end=to;this.unit=unit||this.unit||"px";this.now=this.start;this.pos=this.state=0;this.update();var self=this;function t(gotoEnd){return self.step(gotoEnd);}t.elem=this.elem;jQuery.timers.push(t);if(jQuery.timerId==null){jQuery.timerId=setInterval(function(){var timers=jQuery.timers;for(var i=0;i<timers.length;i++)if(!timers[i]())timers.splice(i--,1);if(!timers.length){clearInterval(jQuery.timerId);jQuery.timerId=null;}},13);}},show:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.show=true;this.custom(0,this.cur());if(this.prop=="width"||this.prop=="height")this.elem.style[this.prop]="1px";jQuery(this.elem).show();},hide:function(){this.options.orig[this.prop]=jQuery.attr(this.elem.style,this.prop);this.options.hide=true;this.custom(this.cur(),0);},step:function(gotoEnd){var t=now();if(gotoEnd||t>this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;var done=true;for(var i in this.options.curAnim)if(this.options.curAnim[i]!==true)done=false;if(done){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;this.elem.style.display=this.options.display;if(jQuery.css(this.elem,"display")=="none")this.elem.style.display="block";}if(this.options.hide)this.elem.style.display="none";if(this.options.hide||this.options.show)for(var p in this.options.curAnim)jQuery.attr(this.elem.style,p,this.options.orig[p]);}if(done)this.options.complete.call(this.elem);return false;}else{var n=t-this.startTime;this.state=n/this.options.duration;this.pos=jQuery.easing[this.options.easing||(jQuery.easing.swing?"swing":"linear")](this.state,n,0,1,this.options.duration);this.now=this.start+((this.end-this.start)*this.pos);this.update();}return true;}};jQuery.extend(jQuery.fx,{speeds:{slow:600,fast:200,def:400},step:{scrollLeft:function(fx){fx.elem.scrollLeft=fx.now;},scrollTop:function(fx){fx.elem.scrollTop=fx.now;},opacity:function(fx){jQuery.attr(fx.elem.style,"opacity",fx.now);},_default:function(fx){fx.elem.style[fx.prop]=fx.now+fx.unit;}}});jQuery.fn.offset=function(){var left=0,top=0,elem=this[0],results;if(elem)with(jQuery.browser){var parent=elem.parentNode,offsetChild=elem,offsetParent=elem.offsetParent,doc=elem.ownerDocument,safari2=safari&&parseInt(version)<522&&!/adobeair/i.test(userAgent),css=jQuery.curCSS,fixed=css(elem,"position")=="fixed";if(elem.getBoundingClientRect){var box=elem.getBoundingClientRect();add(box.left+Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),box.top+Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));add(-doc.documentElement.clientLeft,-doc.documentElement.clientTop);}else{add(elem.offsetLeft,elem.offsetTop);while(offsetParent){add(offsetParent.offsetLeft,offsetParent.offsetTop);if(mozilla&&!/^t(able|d|h)$/i.test(offsetParent.tagName)||safari&&!safari2)border(offsetParent);if(!fixed&&css(offsetParent,"position")=="fixed")fixed=true;offsetChild=/^body$/i.test(offsetParent.tagName)?offsetChild:offsetParent;offsetParent=offsetParent.offsetParent;}while(parent&&parent.tagName&&!/^body|html$/i.test(parent.tagName)){if(!/^inline|table.*$/i.test(css(parent,"display")))add(-parent.scrollLeft,-parent.scrollTop);if(mozilla&&css(parent,"overflow")!="visible")border(parent);parent=parent.parentNode;}if((safari2&&(fixed||css(offsetChild,"position")=="absolute"))||(mozilla&&css(offsetChild,"position")!="absolute"))add(-doc.body.offsetLeft,-doc.body.offsetTop);if(fixed)add(Math.max(doc.documentElement.scrollLeft,doc.body.scrollLeft),Math.max(doc.documentElement.scrollTop,doc.body.scrollTop));}results={top:top,left:left};}function border(elem){add(jQuery.curCSS(elem,"borderLeftWidth",true),jQuery.curCSS(elem,"borderTopWidth",true));}function add(l,t){left+=parseInt(l,10)||0;top+=parseInt(t,10)||0;}return results;};jQuery.fn.extend({position:function(){var left=0,top=0,results;if(this[0]){var offsetParent=this.offsetParent(),offset=this.offset(),parentOffset=/^body|html$/i.test(offsetParent[0].tagName)?{top:0,left:0}:offsetParent.offset();offset.top-=num(this,'marginTop');offset.left-=num(this,'marginLeft');parentOffset.top+=num(offsetParent,'borderTopWidth');parentOffset.left+=num(offsetParent,'borderLeftWidth');results={top:offset.top-parentOffset.top,left:offset.left-parentOffset.left};}return results;},offsetParent:function(){var offsetParent=this[0].offsetParent;while(offsetParent&&(!/^body|html$/i.test(offsetParent.tagName)&&jQuery.css(offsetParent,'position')=='static'))offsetParent=offsetParent.offsetParent;return jQuery(offsetParent);}});jQuery.each(['Left','Top'],function(i,name){var method='scroll'+name;jQuery.fn[method]=function(val){if(!this[0])return;return val!=undefined?this.each(function(){this==window||this==document?window.scrollTo(!i?val:jQuery(window).scrollLeft(),i?val:jQuery(window).scrollTop()):this[method]=val;}):this[0]==window||this[0]==document?self[i?'pageYOffset':'pageXOffset']||jQuery.boxModel&&document.documentElement[method]||document.body[method]:this[0][method];};});jQuery.each(["Height","Width"],function(i,name){var tl=i?"Left":"Top",br=i?"Right":"Bottom";jQuery.fn["inner"+name]=function(){return this[name.toLowerCase()]()+num(this,"padding"+tl)+num(this,"padding"+br);};jQuery.fn["outer"+name]=function(margin){return this["inner"+name]()+num(this,"border"+tl+"Width")+num(this,"border"+br+"Width")+(margin?num(this,"margin"+tl)+num(this,"margin"+br):0);};});})();
+
+;(function($){var _remove=$.fn.remove;$.fn.remove=function(){$("*",this).add(this).triggerHandler("remove");return _remove.apply(this,arguments);};function isVisible(element){function checkStyles(element){var style=element.style;return(style.display!='none'&&style.visibility!='hidden');}
+var visible=checkStyles(element);(visible&&$.each($.dir(element,'parentNode'),function(){return(visible=checkStyles(this));}));return visible;}
+$.extend($.expr[':'],{data:function(a,i,m){return $.data(a,m[3]);},tabbable:function(a,i,m){var nodeName=a.nodeName.toLowerCase();return(a.tabIndex>=0&&(('a'==nodeName&&a.href)||(/input|select|textarea|button/.test(nodeName)&&'hidden'!=a.type&&!a.disabled))&&isVisible(a));}});$.keyCode={BACKSPACE:8,CAPS_LOCK:20,COMMA:188,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106,NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38};function getter(namespace,plugin,method,args){function getMethods(type){var methods=$[namespace][plugin][type]||[];return(typeof methods=='string'?methods.split(/,?\s+/):methods);}
+var methods=getMethods('getter');if(args.length==1&&typeof args[0]=='string'){methods=methods.concat(getMethods('getterSetter'));}
+return($.inArray(method,methods)!=-1);}
+$.widget=function(name,prototype){var namespace=name.split(".")[0];name=name.split(".")[1];$.fn[name]=function(options){var isMethodCall=(typeof options=='string'),args=Array.prototype.slice.call(arguments,1);if(isMethodCall&&options.substring(0,1)=='_'){return this;}
+if(isMethodCall&&getter(namespace,name,options,args)){var instance=$.data(this[0],name);return(instance?instance[options].apply(instance,args):undefined);}
+return this.each(function(){var instance=$.data(this,name);(!instance&&!isMethodCall&&$.data(this,name,new $[namespace][name](this,options)));(instance&&isMethodCall&&$.isFunction(instance[options])&&instance[options].apply(instance,args));});};$[namespace][name]=function(element,options){var self=this;this.widgetName=name;this.widgetEventPrefix=$[namespace][name].eventPrefix||name;this.widgetBaseClass=namespace+'-'+name;this.options=$.extend({},$.widget.defaults,$[namespace][name].defaults,$.metadata&&$.metadata.get(element)[name],options);this.element=$(element).bind('setData.'+name,function(e,key,value){return self._setData(key,value);}).bind('getData.'+name,function(e,key){return self._getData(key);}).bind('remove',function(){return self.destroy();});this._init();};$[namespace][name].prototype=$.extend({},$.widget.prototype,prototype);$[namespace][name].getterSetter='option';};$.widget.prototype={_init:function(){},destroy:function(){this.element.removeData(this.widgetName);},option:function(key,value){var options=key,self=this;if(typeof key=="string"){if(value===undefined){return this._getData(key);}
+options={};options[key]=value;}
+$.each(options,function(key,value){self._setData(key,value);});},_getData:function(key){return this.options[key];},_setData:function(key,value){this.options[key]=value;if(key=='disabled'){this.element[value?'addClass':'removeClass'](this.widgetBaseClass+'-disabled');}},enable:function(){this._setData('disabled',false);},disable:function(){this._setData('disabled',true);},_trigger:function(type,e,data){var eventName=(type==this.widgetEventPrefix?type:this.widgetEventPrefix+type);e=e||$.event.fix({type:eventName,target:this.element[0]});return this.element.triggerHandler(eventName,[e,data],this.options[type]);}};$.widget.defaults={disabled:false};$.ui={plugin:{add:function(module,option,set){var proto=$.ui[module].prototype;for(var i in set){proto.plugins[i]=proto.plugins[i]||[];proto.plugins[i].push([option,set[i]]);}},call:function(instance,name,args){var set=instance.plugins[name];if(!set){return;}
+for(var i=0;i<set.length;i++){if(instance.options[set[i][0]]){set[i][1].apply(instance.element,args);}}}},cssCache:{},css:function(name){if($.ui.cssCache[name]){return $.ui.cssCache[name];}
+var tmp=$('<div class="ui-gen">').addClass(name).css({position:'absolute',top:'-5000px',left:'-5000px',display:'block'}).appendTo('body');$.ui.cssCache[name]=!!((!(/auto|default/).test(tmp.css('cursor'))||(/^[1-9]/).test(tmp.css('height'))||(/^[1-9]/).test(tmp.css('width'))||!(/none/).test(tmp.css('backgroundImage'))||!(/transparent|rgba\(0, 0, 0, 0\)/).test(tmp.css('backgroundColor'))));try{$('body').get(0).removeChild(tmp.get(0));}catch(e){}
+return $.ui.cssCache[name];},disableSelection:function(el){return $(el).attr('unselectable','on').css('MozUserSelect','none').bind('selectstart.ui',function(){return false;});},enableSelection:function(el){return $(el).attr('unselectable','off').css('MozUserSelect','').unbind('selectstart.ui');},hasScroll:function(e,a){if($(e).css('overflow')=='hidden'){return false;}
+var scroll=(a&&a=='left')?'scrollLeft':'scrollTop',has=false;if(e[scroll]>0){return true;}
+e[scroll]=1;has=(e[scroll]>0);e[scroll]=0;return has;}};$.ui.mouse={_mouseInit:function(){var self=this;this.element.bind('mousedown.'+this.widgetName,function(e){return self._mouseDown(e);});if($.browser.msie){this._mouseUnselectable=this.element.attr('unselectable');this.element.attr('unselectable','on');}
+this.started=false;},_mouseDestroy:function(){this.element.unbind('.'+this.widgetName);($.browser.msie&&this.element.attr('unselectable',this._mouseUnselectable));},_mouseDown:function(e){(this._mouseStarted&&this._mouseUp(e));this._mouseDownEvent=e;var self=this,btnIsLeft=(e.which==1),elIsCancel=(typeof this.options.cancel=="string"?$(e.target).parents().add(e.target).filter(this.options.cancel).length:false);if(!btnIsLeft||elIsCancel||!this._mouseCapture(e)){return true;}
+this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet){this._mouseDelayTimer=setTimeout(function(){self.mouseDelayMet=true;},this.options.delay);}
+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(e)!==false);if(!this._mouseStarted){e.preventDefault();return true;}}
+this._mouseMoveDelegate=function(e){return self._mouseMove(e);};this._mouseUpDelegate=function(e){return self._mouseUp(e);};$(document).bind('mousemove.'+this.widgetName,this._mouseMoveDelegate).bind('mouseup.'+this.widgetName,this._mouseUpDelegate);return false;},_mouseMove:function(e){if($.browser.msie&&!e.button){return this._mouseUp(e);}
+if(this._mouseStarted){this._mouseDrag(e);return false;}
+if(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)){this._mouseStarted=(this._mouseStart(this._mouseDownEvent,e)!==false);(this._mouseStarted?this._mouseDrag(e):this._mouseUp(e));}
+return!this._mouseStarted;},_mouseUp:function(e){$(document).unbind('mousemove.'+this.widgetName,this._mouseMoveDelegate).unbind('mouseup.'+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted=false;this._mouseStop(e);}
+return false;},_mouseDistanceMet:function(e){return(Math.max(Math.abs(this._mouseDownEvent.pageX-e.pageX),Math.abs(this._mouseDownEvent.pageY-e.pageY))>=this.options.distance);},_mouseDelayMet:function(e){return this.mouseDelayMet;},_mouseStart:function(e){},_mouseDrag:function(e){},_mouseStop:function(e){},_mouseCapture:function(e){return true;}};$.ui.mouse.defaults={cancel:null,distance:1,delay:0};})(jQuery);(function($){$.widget("ui.resizable",$.extend({},$.ui.mouse,{_init:function(){var self=this,o=this.options;var elpos=this.element.css('position');this.originalElement=this.element;this.element.addClass("ui-resizable").css({position:/static/.test(elpos)?'relative':elpos});$.extend(o,{_aspectRatio:!!(o.aspectRatio),helper:o.helper||o.ghost||o.animate?o.helper||'proxy':null,knobHandles:o.knobHandles===true?'ui-resizable-knob-handle':o.knobHandles});var aBorder='1px solid #DEDEDE';o.defaultTheme={'ui-resizable':{display:'block'},'ui-resizable-handle':{position:'absolute',background:'#F2F2F2',fontSize:'0.1px'},'ui-resizable-n':{cursor:'n-resize',height:'4px',left:'0px',right:'0px',borderTop:aBorder},'ui-resizable-s':{cursor:'s-resize',height:'4px',left:'0px',right:'0px',borderBottom:aBorder},'ui-resizable-e':{cursor:'e-resize',width:'4px',top:'0px',bottom:'0px',borderRight:aBorder},'ui-resizable-w':{cursor:'w-resize',width:'4px',top:'0px',bottom:'0px',borderLeft:aBorder},'ui-resizable-se':{cursor:'se-resize',width:'4px',height:'4px',borderRight:aBorder,borderBottom:aBorder},'ui-resizable-sw':{cursor:'sw-resize',width:'4px',height:'4px',borderBottom:aBorder,borderLeft:aBorder},'ui-resizable-ne':{cursor:'ne-resize',width:'4px',height:'4px',borderRight:aBorder,borderTop:aBorder},'ui-resizable-nw':{cursor:'nw-resize',width:'4px',height:'4px',borderLeft:aBorder,borderTop:aBorder}};o.knobTheme={'ui-resizable-handle':{background:'#F2F2F2',border:'1px solid #808080',height:'8px',width:'8px'},'ui-resizable-n':{cursor:'n-resize',top:'0px',left:'45%'},'ui-resizable-s':{cursor:'s-resize',bottom:'0px',left:'45%'},'ui-resizable-e':{cursor:'e-resize',right:'0px',top:'45%'},'ui-resizable-w':{cursor:'w-resize',left:'0px',top:'45%'},'ui-resizable-se':{cursor:'se-resize',right:'0px',bottom:'0px'},'ui-resizable-sw':{cursor:'sw-resize',left:'0px',bottom:'0px'},'ui-resizable-nw':{cursor:'nw-resize',left:'0px',top:'0px'},'ui-resizable-ne':{cursor:'ne-resize',right:'0px',top:'0px'}};o._nodeName=this.element[0].nodeName;if(o._nodeName.match(/canvas|textarea|input|select|button|img/i)){var el=this.element;if(/relative/.test(el.css('position'))&&$.browser.opera)
+el.css({position:'relative',top:'auto',left:'auto'});el.wrap($('<div class="ui-wrapper" style="overflow: hidden;"></div>').css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')}));var oel=this.element;this.element=this.element.parent();this.element.data('resizable',this);this.element.css({marginLeft:oel.css("marginLeft"),marginTop:oel.css("marginTop"),marginRight:oel.css("marginRight"),marginBottom:oel.css("marginBottom")});oel.css({marginLeft:0,marginTop:0,marginRight:0,marginBottom:0});if($.browser.safari&&o.preventDefault)oel.css('resize','none');o.proportionallyResize=oel.css({position:'static',zoom:1,display:'block'});this.element.css({margin:oel.css('margin')});this._proportionallyResize();}
+if(!o.handles)o.handles=!$('.ui-resizable-handle',this.element).length?"e,s,se":{n:'.ui-resizable-n',e:'.ui-resizable-e',s:'.ui-resizable-s',w:'.ui-resizable-w',se:'.ui-resizable-se',sw:'.ui-resizable-sw',ne:'.ui-resizable-ne',nw:'.ui-resizable-nw'};if(o.handles.constructor==String){o.zIndex=o.zIndex||1000;if(o.handles=='all')o.handles='n,e,s,w,se,sw,ne,nw';var n=o.handles.split(",");o.handles={};var insertionsDefault={handle:'position: absolute; display: none; overflow:hidden;',n:'top: 0pt; width:100%;',e:'right: 0pt; height:100%;',s:'bottom: 0pt; width:100%;',w:'left: 0pt; height:100%;',se:'bottom: 0pt; right: 0px;',sw:'bottom: 0pt; left: 0px;',ne:'top: 0pt; right: 0px;',nw:'top: 0pt; left: 0px;'};for(var i=0;i<n.length;i++){var handle=$.trim(n[i]),dt=o.defaultTheme,hname='ui-resizable-'+handle,loadDefault=!$.ui.css(hname)&&!o.knobHandles,userKnobClass=$.ui.css('ui-resizable-knob-handle'),allDefTheme=$.extend(dt[hname],dt['ui-resizable-handle']),allKnobTheme=$.extend(o.knobTheme[hname],!userKnobClass?o.knobTheme['ui-resizable-handle']:{});var applyZIndex=/sw|se|ne|nw/.test(handle)?{zIndex:++o.zIndex}:{};var defCss=(loadDefault?insertionsDefault[handle]:''),axis=$(['<div class="ui-resizable-handle ',hname,'" style="',defCss,insertionsDefault.handle,'"></div>'].join('')).css(applyZIndex);o.handles[handle]='.ui-resizable-'+handle;this.element.append(axis.css(loadDefault?allDefTheme:{}).css(o.knobHandles?allKnobTheme:{}).addClass(o.knobHandles?'ui-resizable-knob-handle':'').addClass(o.knobHandles));}
+if(o.knobHandles)this.element.addClass('ui-resizable-knob').css(!$.ui.css('ui-resizable-knob')?{}:{});}
+this._renderAxis=function(target){target=target||this.element;for(var i in o.handles){if(o.handles[i].constructor==String)
+o.handles[i]=$(o.handles[i],this.element).show();if(o.transparent)
+o.handles[i].css({opacity:0});if(this.element.is('.ui-wrapper')&&o._nodeName.match(/textarea|input|select|button/i)){var axis=$(o.handles[i],this.element),padWrapper=0;padWrapper=/sw|ne|nw|se|n|s/.test(i)?axis.outerHeight():axis.outerWidth();var padPos=['padding',/ne|nw|n/.test(i)?'Top':/se|sw|s/.test(i)?'Bottom':/^e$/.test(i)?'Right':'Left'].join("");if(!o.transparent)
+target.css(padPos,padWrapper);this._proportionallyResize();}
+if(!$(o.handles[i]).length)continue;}};this._renderAxis(this.element);o._handles=$('.ui-resizable-handle',self.element);if(o.disableSelection)
+o._handles.each(function(i,e){$.ui.disableSelection(e);});o._handles.mouseover(function(){if(!o.resizing){if(this.className)
+var axis=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);self.axis=o.axis=axis&&axis[1]?axis[1]:'se';}});if(o.autoHide){o._handles.hide();$(self.element).addClass("ui-resizable-autohide").hover(function(){$(this).removeClass("ui-resizable-autohide");o._handles.show();},function(){if(!o.resizing){$(this).addClass("ui-resizable-autohide");o._handles.hide();}});}
+this._mouseInit();},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,options:this.options,originalSize:this.originalSize,originalPosition:this.originalPosition};},_propagate:function(n,e){$.ui.plugin.call(this,n,[e,this.ui()]);if(n!="resize")this.element.triggerHandler(["resize",n].join(""),[e,this.ui()],this.options[n]);},destroy:function(){var el=this.element,wrapped=el.children(".ui-resizable").get(0);this._mouseDestroy();var _destroy=function(exp){$(exp).removeClass("ui-resizable ui-resizable-disabled").removeData("resizable").unbind(".resizable").find('.ui-resizable-handle').remove();};_destroy(el);if(el.is('.ui-wrapper')&&wrapped){el.parent().append($(wrapped).css({position:el.css('position'),width:el.outerWidth(),height:el.outerHeight(),top:el.css('top'),left:el.css('left')})).end().remove();_destroy(wrapped);}},_mouseCapture:function(e){if(this.options.disabled)return false;var handle=false;for(var i in this.options.handles){if($(this.options.handles[i])[0]==e.target)handle=true;}
+if(!handle)return false;return true;},_mouseStart:function(e){var o=this.options,iniPos=this.element.position(),el=this.element,num=function(v){return parseInt(v,10)||0;},ie6=$.browser.msie&&$.browser.version<7;o.resizing=true;o.documentScroll={top:$(document).scrollTop(),left:$(document).scrollLeft()};if(el.is('.ui-draggable')||(/absolute/).test(el.css('position'))){var sOffset=$.browser.msie&&!o.containment&&(/absolute/).test(el.css('position'))&&!(/relative/).test(el.parent().css('position'));var dscrollt=sOffset?o.documentScroll.top:0,dscrolll=sOffset?o.documentScroll.left:0;el.css({position:'absolute',top:(iniPos.top+dscrollt),left:(iniPos.left+dscrolll)});}
+if($.browser.opera&&/relative/.test(el.css('position')))
+el.css({position:'relative',top:'auto',left:'auto'});this._renderProxy();var curleft=num(this.helper.css('left')),curtop=num(this.helper.css('top'));if(o.containment){curleft+=$(o.containment).scrollLeft()||0;curtop+=$(o.containment).scrollTop()||0;}
+this.offset=this.helper.offset();this.position={left:curleft,top:curtop};this.size=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalSize=o.helper||ie6?{width:el.outerWidth(),height:el.outerHeight()}:{width:el.width(),height:el.height()};this.originalPosition={left:curleft,top:curtop};this.sizeDiff={width:el.outerWidth()-el.width(),height:el.outerHeight()-el.height()};this.originalMousePosition={left:e.pageX,top:e.pageY};o.aspectRatio=(typeof o.aspectRatio=='number')?o.aspectRatio:((this.originalSize.width/this.originalSize.height)||1);if(o.preserveCursor)
+$('body').css('cursor',this.axis+'-resize');this._propagate("start",e);return true;},_mouseDrag:function(e){var el=this.helper,o=this.options,props={},self=this,smp=this.originalMousePosition,a=this.axis;var dx=(e.pageX-smp.left)||0,dy=(e.pageY-smp.top)||0;var trigger=this._change[a];if(!trigger)return false;var data=trigger.apply(this,[e,dx,dy]),ie6=$.browser.msie&&$.browser.version<7,csdif=this.sizeDiff;if(o._aspectRatio||e.shiftKey)
+data=this._updateRatio(data,e);data=this._respectSize(data,e);this._propagate("resize",e);el.css({top:this.position.top+"px",left:this.position.left+"px",width:this.size.width+"px",height:this.size.height+"px"});if(!o.helper&&o.proportionallyResize)
+this._proportionallyResize();this._updateCache(data);this.element.triggerHandler("resize",[e,this.ui()],this.options["resize"]);return false;},_mouseStop:function(e){this.options.resizing=false;var o=this.options,num=function(v){return parseInt(v,10)||0;},self=this;if(o.helper){var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var s={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;if(!o.animate)
+this.element.css($.extend(s,{top:top,left:left}));if(o.helper&&!o.animate)this._proportionallyResize();}
+if(o.preserveCursor)
+$('body').css('cursor','auto');this._propagate("stop",e);if(o.helper)this.helper.remove();return false;},_updateCache:function(data){var o=this.options;this.offset=this.helper.offset();if(data.left)this.position.left=data.left;if(data.top)this.position.top=data.top;if(data.height)this.size.height=data.height;if(data.width)this.size.width=data.width;},_updateRatio:function(data,e){var o=this.options,cpos=this.position,csize=this.size,a=this.axis;if(data.height)data.width=(csize.height*o.aspectRatio);else if(data.width)data.height=(csize.width/o.aspectRatio);if(a=='sw'){data.left=cpos.left+(csize.width-data.width);data.top=null;}
+if(a=='nw'){data.top=cpos.top+(csize.height-data.height);data.left=cpos.left+(csize.width-data.width);}
+return data;},_respectSize:function(data,e){var el=this.helper,o=this.options,pRatio=o._aspectRatio||e.shiftKey,a=this.axis,ismaxw=data.width&&o.maxWidth&&o.maxWidth<data.width,ismaxh=data.height&&o.maxHeight&&o.maxHeight<data.height,isminw=data.width&&o.minWidth&&o.minWidth>data.width,isminh=data.height&&o.minHeight&&o.minHeight>data.height;if(isminw)data.width=o.minWidth;if(isminh)data.height=o.minHeight;if(ismaxw)data.width=o.maxWidth;if(ismaxh)data.height=o.maxHeight;var dw=this.originalPosition.left+this.originalSize.width,dh=this.position.top+this.size.height;var cw=/sw|nw|w/.test(a),ch=/nw|ne|n/.test(a);if(isminw&&cw)data.left=dw-o.minWidth;if(ismaxw&&cw)data.left=dw-o.maxWidth;if(isminh&&ch)data.top=dh-o.minHeight;if(ismaxh&&ch)data.top=dh-o.maxHeight;var isNotwh=!data.width&&!data.height;if(isNotwh&&!data.left&&data.top)data.top=null;else if(isNotwh&&!data.top&&data.left)data.left=null;return data;},_proportionallyResize:function(){var o=this.options;if(!o.proportionallyResize)return;var prel=o.proportionallyResize,el=this.helper||this.element;if(!o.borderDif){var b=[prel.css('borderTopWidth'),prel.css('borderRightWidth'),prel.css('borderBottomWidth'),prel.css('borderLeftWidth')],p=[prel.css('paddingTop'),prel.css('paddingRight'),prel.css('paddingBottom'),prel.css('paddingLeft')];o.borderDif=$.map(b,function(v,i){var border=parseInt(v,10)||0,padding=parseInt(p[i],10)||0;return border+padding;});}
+prel.css({height:(el.height()-o.borderDif[0]-o.borderDif[2])+"px",width:(el.width()-o.borderDif[1]-o.borderDif[3])+"px"});},_renderProxy:function(){var el=this.element,o=this.options;this.elementOffset=el.offset();if(o.helper){this.helper=this.helper||$('<div style="overflow:hidden;"></div>');var ie6=$.browser.msie&&$.browser.version<7,ie6offset=(ie6?1:0),pxyoffset=(ie6?2:-1);this.helper.addClass(o.helper).css({width:el.outerWidth()+pxyoffset,height:el.outerHeight()+pxyoffset,position:'absolute',left:this.elementOffset.left-ie6offset+'px',top:this.elementOffset.top-ie6offset+'px',zIndex:++o.zIndex});this.helper.appendTo("body");if(o.disableSelection)
+$.ui.disableSelection(this.helper.get(0));}else{this.helper=el;}},_change:{e:function(e,dx,dy){return{width:this.originalSize.width+dx};},w:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{left:sp.left+dx,width:cs.width-dx};},n:function(e,dx,dy){var o=this.options,cs=this.originalSize,sp=this.originalPosition;return{top:sp.top+dy,height:cs.height-dy};},s:function(e,dx,dy){return{height:this.originalSize.height+dy};},se:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},sw:function(e,dx,dy){return $.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));},ne:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,dx,dy]));},nw:function(e,dx,dy){return $.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,dx,dy]));}}}));$.extend($.ui.resizable,{defaults:{cancel:":input",distance:1,delay:0,preventDefault:true,transparent:false,minWidth:10,minHeight:10,aspectRatio:false,disableSelection:true,preserveCursor:true,autoHide:false,knobHandles:false}});$.ui.plugin.add("resizable","containment",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),el=self.element;var oc=o.containment,ce=(oc instanceof $)?oc.get(0):(/parent/.test(oc))?el.parent().get(0):oc;if(!ce)return;self.containerElement=$(ce);if(/document/.test(oc)||oc==document){self.containerOffset={left:0,top:0};self.containerPosition={left:0,top:0};self.parentData={element:$(document),left:0,top:0,width:$(document).width(),height:$(document).height()||document.body.parentNode.scrollHeight};}
+else{self.containerOffset=$(ce).offset();self.containerPosition=$(ce).position();self.containerSize={height:$(ce).innerHeight(),width:$(ce).innerWidth()};var co=self.containerOffset,ch=self.containerSize.height,cw=self.containerSize.width,width=($.ui.hasScroll(ce,"left")?ce.scrollWidth:cw),height=($.ui.hasScroll(ce)?ce.scrollHeight:ch);self.parentData={element:ce,left:co.left,top:co.top,width:width,height:height};}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),ps=self.containerSize,co=self.containerOffset,cs=self.size,cp=self.position,pRatio=o._aspectRatio||e.shiftKey,cop={top:0,left:0},ce=self.containerElement;if(ce[0]!=document&&/static/.test(ce.css('position')))
+cop=self.containerPosition;if(cp.left<(o.helper?co.left:cop.left)){self.size.width=self.size.width+(o.helper?(self.position.left-co.left):(self.position.left-cop.left));if(pRatio)self.size.height=self.size.width/o.aspectRatio;self.position.left=o.helper?co.left:cop.left;}
+if(cp.top<(o.helper?co.top:0)){self.size.height=self.size.height+(o.helper?(self.position.top-co.top):self.position.top);if(pRatio)self.size.width=self.size.height*o.aspectRatio;self.position.top=o.helper?co.top:0;}
+var woset=(o.helper?self.offset.left-co.left:(self.position.left-cop.left))+self.sizeDiff.width,hoset=(o.helper?self.offset.top-co.top:self.position.top)+self.sizeDiff.height;if(woset+self.size.width>=self.parentData.width){self.size.width=self.parentData.width-woset;if(pRatio)self.size.height=self.size.width/o.aspectRatio;}
+if(hoset+self.size.height>=self.parentData.height){self.size.height=self.parentData.height-hoset;if(pRatio)self.size.width=self.size.height*o.aspectRatio;}},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cp=self.position,co=self.containerOffset,cop=self.containerPosition,ce=self.containerElement;var helper=$(self.helper),ho=helper.offset(),w=helper.innerWidth(),h=helper.innerHeight();if(o.helper&&!o.animate&&/relative/.test(ce.css('position')))
+$(this).css({left:(ho.left-co.left),top:(ho.top-co.top),width:w,height:h});if(o.helper&&!o.animate&&/static/.test(ce.css('position')))
+$(this).css({left:cop.left+(ho.left-co.left),top:cop.top+(ho.top-co.top),width:w,height:h});}});$.ui.plugin.add("resizable","grid",{resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),cs=self.size,os=self.originalSize,op=self.originalPosition,a=self.axis,ratio=o._aspectRatio||e.shiftKey;o.grid=typeof o.grid=="number"?[o.grid,o.grid]:o.grid;var ox=Math.round((cs.width-os.width)/(o.grid[0]||1))*(o.grid[0]||1),oy=Math.round((cs.height-os.height)/(o.grid[1]||1))*(o.grid[1]||1);if(/^(se|s|e)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;}
+else if(/^(ne)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;}
+else if(/^(sw)$/.test(a)){self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.left=op.left-ox;}
+else{self.size.width=os.width+ox;self.size.height=os.height+oy;self.position.top=op.top-oy;self.position.left=op.left-ox;}}});$.ui.plugin.add("resizable","animate",{stop:function(e,ui){var o=ui.options,self=$(this).data("resizable");var pr=o.proportionallyResize,ista=pr&&(/textarea/i).test(pr.get(0).nodeName),soffseth=ista&&$.ui.hasScroll(pr.get(0),'left')?0:self.sizeDiff.height,soffsetw=ista?0:self.sizeDiff.width;var style={width:(self.size.width-soffsetw),height:(self.size.height-soffseth)},left=(parseInt(self.element.css('left'),10)+(self.position.left-self.originalPosition.left))||null,top=(parseInt(self.element.css('top'),10)+(self.position.top-self.originalPosition.top))||null;self.element.animate($.extend(style,top&&left?{top:top,left:left}:{}),{duration:o.animateDuration||"slow",easing:o.animateEasing||"swing",step:function(){var data={width:parseInt(self.element.css('width'),10),height:parseInt(self.element.css('height'),10),top:parseInt(self.element.css('top'),10),left:parseInt(self.element.css('left'),10)};if(pr)pr.css({width:data.width,height:data.height});self._updateCache(data);self._propagate("animate",e);}});}});$.ui.plugin.add("resizable","ghost",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize,cs=self.size;if(!pr)self.ghost=self.element.clone();else self.ghost=pr.clone();self.ghost.css({opacity:.25,display:'block',position:'relative',height:cs.height,width:cs.width,margin:0,left:0,top:0}).addClass('ui-resizable-ghost').addClass(typeof o.ghost=='string'?o.ghost:'');self.ghost.appendTo(self.helper);},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost)self.ghost.css({position:'relative',height:self.size.height,width:self.size.width});},stop:function(e,ui){var o=ui.options,self=$(this).data("resizable"),pr=o.proportionallyResize;if(self.ghost&&self.helper)self.helper.get(0).removeChild(self.ghost.get(0));}});$.ui.plugin.add("resizable","alsoResize",{start:function(e,ui){var o=ui.options,self=$(this).data("resizable"),_store=function(exp){$(exp).each(function(){$(this).data("resizable-alsoresize",{width:parseInt($(this).width(),10),height:parseInt($(this).height(),10),left:parseInt($(this).css('left'),10),top:parseInt($(this).css('top'),10)});});};if(typeof(o.alsoResize)=='object'){if(o.alsoResize.length){o.alsoResize=o.alsoResize[0];_store(o.alsoResize);}
+else{$.each(o.alsoResize,function(exp,c){_store(exp);});}}else{_store(o.alsoResize);}},resize:function(e,ui){var o=ui.options,self=$(this).data("resizable"),os=self.originalSize,op=self.originalPosition;var delta={height:(self.size.height-os.height)||0,width:(self.size.width-os.width)||0,top:(self.position.top-op.top)||0,left:(self.position.left-op.left)||0},_alsoResize=function(exp,c){$(exp).each(function(){var start=$(this).data("resizable-alsoresize"),style={},css=c&&c.length?c:['width','height','top','left'];$.each(css||['width','height','top','left'],function(i,prop){var sum=(start[prop]||0)+(delta[prop]||0);if(sum&&sum>=0)
+style[prop]=sum||null;});$(this).css(style);});};if(typeof(o.alsoResize)=='object'){$.each(o.alsoResize,function(exp,c){_alsoResize(exp,c);});}else{_alsoResize(o.alsoResize);}},stop:function(e,ui){$(this).removeData("resizable-alsoresize-start");}});})(jQuery); \ No newline at end of file
diff --git a/tools/droiddoc/templates/assets/navtree.js b/tools/droiddoc/templates/assets/navtree.js
new file mode 100644
index 000000000..f48e1dcad
--- /dev/null
+++ b/tools/droiddoc/templates/assets/navtree.js
@@ -0,0 +1,179 @@
+
+function new_node(me, mom, text, link, children_data)
+{
+ var node = new Object();
+ node.children = Array();
+ node.children_data = children_data;
+ node.depth = mom.depth + 1;
+
+ node.li = document.createElement("li");
+ mom.get_children_ul().appendChild(node.li);
+
+ node.label_div = document.createElement("div");
+ node.li.appendChild(node.label_div);
+ node.label_div.style.paddingLeft = 10*node.depth + "px";
+ node.label_div.className = "label";
+
+ if (children_data == null) {
+ // 12 is the width of the triangle and padding extra space
+ node.label_div.style.paddingLeft = ((10*node.depth)+12) + "px";
+ } else {
+ node.label_div.style.paddingLeft = 10*node.depth + "px";
+ node.expand_toggle = document.createElement("a");
+ node.expand_toggle.href = "javascript:void(0)";
+ node.expand_toggle.onclick = function() {
+ if (node.expanded) {
+ $(node.get_children_ul()).slideUp("fast");
+ node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+ node.expanded = false;
+ } else {
+ expand_node(me, node);
+ }
+ };
+ node.label_div.appendChild(node.expand_toggle);
+
+ node.plus_img = document.createElement("img");
+ node.plus_img.src = me.toroot + "assets/images/triangle-closed-small.png";
+ node.plus_img.className = "plus";
+ node.plus_img.border = "0";
+ node.expand_toggle.appendChild(node.plus_img);
+
+ node.expanded = false;
+ }
+
+ var a = document.createElement("a");
+ node.label_div.appendChild(a);
+ node.label = document.createTextNode(text);
+ a.appendChild(node.label);
+ if (link) {
+ a.href = me.toroot + link;
+ } else {
+ if (children_data != null) {
+ a.className = "nolink";
+ a.href = "javascript:void(0)";
+ a.onclick = node.expand_toggle.onclick;
+ // This next line shouldn't be necessary. I'll buy a beer for the first
+ // person who figures out how to remove this line and have the link
+ // toggle shut on the first try. --joeo@android.com
+ node.expanded = false;
+ }
+ }
+
+
+ node.children_ul = null;
+ node.get_children_ul = function() {
+ if (!node.children_ul) {
+ node.children_ul = document.createElement("ul");
+ node.children_ul.className = "children_ul";
+ node.children_ul.style.display = "none";
+ node.li.appendChild(node.children_ul);
+ }
+ return node.children_ul;
+ };
+
+ return node;
+}
+
+function expand_node(me, node)
+{
+ if (node.children_data && !node.expanded) {
+ if (node.children_visited) {
+ $(node.get_children_ul()).slideDown("fast");
+ } else {
+ get_node(me, node);
+ $(node.get_children_ul()).slideDown("fast");
+ }
+ node.plus_img.src = me.toroot + "assets/images/triangle-opened-small.png";
+ node.expanded = true;
+ }
+}
+
+function get_node(me, mom)
+{
+ mom.children_visited = true;
+ for (var i in mom.children_data) {
+ var node_data = mom.children_data[i];
+ mom.children[i] = new_node(me, mom, node_data[0], node_data[1],
+ node_data[2]);
+ }
+}
+
+function this_page_relative(toroot)
+{
+ var full = document.location.pathname;
+ var file = "";
+ if (toroot.substr(0, 1) == "/") {
+ if (full.substr(0, toroot.length) == toroot) {
+ return full.substr(toroot.length);
+ } else {
+ // the file isn't under toroot. Fail.
+ return null;
+ }
+ } else {
+ if (toroot != "./") {
+ toroot = "./" + toroot;
+ }
+ do {
+ if (toroot.substr(toroot.length-3, 3) == "../" || toroot == "./") {
+ var pos = full.lastIndexOf("/");
+ file = full.substr(pos) + file;
+ full = full.substr(0, pos);
+ toroot = toroot.substr(0, toroot.length-3);
+ }
+ } while (toroot != "" && toroot != "/");
+ return file.substr(1);
+ }
+}
+
+function find_page(url, data)
+{
+ var nodes = data;
+ var result = null;
+ for (var i in nodes) {
+ var d = nodes[i];
+ if (d[1] == url) {
+ return new Array(i);
+ }
+ else if (d[2] != null) {
+ result = find_page(url, d[2]);
+ if (result != null) {
+ return (new Array(i).concat(result));
+ }
+ }
+ }
+ return null;
+}
+
+function init_navtree(navtree_id, toroot, root_nodes)
+{
+ var me = new Object();
+ me.toroot = toroot;
+ me.node = new Object();
+
+ me.node.li = document.getElementById(navtree_id);
+ me.node.children_data = root_nodes;
+ me.node.children = new Array();
+ me.node.children_ul = document.createElement("ul");
+ me.node.get_children_ul = function() { return me.node.children_ul; };
+ //me.node.children_ul.className = "children_ul";
+ me.node.li.appendChild(me.node.children_ul);
+ me.node.depth = 0;
+
+ get_node(me, me.node);
+
+ me.this_page = this_page_relative(toroot);
+ me.breadcrumbs = find_page(me.this_page, root_nodes);
+ if (me.breadcrumbs != null && me.breadcrumbs.length != 0) {
+ var mom = me.node;
+ for (var i in me.breadcrumbs) {
+ var j = me.breadcrumbs[i];
+ mom = mom.children[j];
+ expand_node(me, mom);
+ }
+ mom.label_div.className = mom.label_div.className + " selected";
+ addLoadEvent(function() {
+ scrollIntoView("nav-tree");
+ });
+ }
+}
+
diff --git a/tools/droiddoc/templates/assets/search_autocomplete.js b/tools/droiddoc/templates/assets/search_autocomplete.js
new file mode 100644
index 000000000..2e12e0fbd
--- /dev/null
+++ b/tools/droiddoc/templates/assets/search_autocomplete.js
@@ -0,0 +1,173 @@
+var gSelectedIndex = -1;
+var gSelectedID = -1;
+var gMatches = new Array();
+var gLastText = "";
+var ROW_COUNT = 30;
+var gInitialized = false;
+var DEFAULT_TEXT = "search developer docs";
+
+function set_row_selected(row, selected)
+{
+ var c1 = row.cells[0];
+ // var c2 = row.cells[1];
+ if (selected) {
+ c1.className = "jd-autocomplete jd-selected";
+ // c2.className = "jd-autocomplete jd-selected jd-linktype";
+ } else {
+ c1.className = "jd-autocomplete";
+ // c2.className = "jd-autocomplete jd-linktype";
+ }
+}
+
+function set_row_values(toroot, row, match)
+{
+ var link = row.cells[0].childNodes[0];
+ link.innerHTML = match.label;
+ link.href = toroot + match.link
+ // row.cells[1].innerHTML = match.type;
+}
+
+function sync_selection_table(toroot)
+{
+ var filtered = document.getElementById("search_filtered");
+ var r; //TR DOM object
+ var i; //TR iterator
+ gSelectedID = -1;
+
+ filtered.onmouseover = function() {
+ if(gSelectedIndex >= 0) {
+ set_row_selected(this.rows[gSelectedIndex], false);
+ gSelectedIndex = -1;
+ }
+ }
+
+ //initialize the table; draw it for the first time (but not visible).
+ if (!gInitialized) {
+ for (i=0; i<ROW_COUNT; i++) {
+ var r = filtered.insertRow(-1);
+ var c1 = r.insertCell(-1);
+ // var c2 = r.insertCell(-1);
+ c1.className = "jd-autocomplete";
+ // c2.className = "jd-autocomplete jd-linktype";
+ var link = document.createElement("a");
+ c1.onmousedown = function() {
+ window.location = this.firstChild.getAttribute("href");
+ }
+ c1.onmouseover = function() {
+ this.className = this.className + " jd-selected";
+ }
+ c1.onmouseout = function() {
+ this.className = "jd-autocomplete";
+ }
+ c1.appendChild(link);
+ }
+ /* var r = filtered.insertRow(-1);
+ var c1 = r.insertCell(-1);
+ c1.className = "jd-autocomplete jd-linktype";
+ c1.colSpan = 2; */
+ gInitialized = true;
+ }
+
+ //if we have results, make the table visible and initialize result info
+ if (gMatches.length > 0) {
+ document.getElementById("search_filtered_div").className = "showing";
+ var N = gMatches.length < ROW_COUNT ? gMatches.length : ROW_COUNT;
+ for (i=0; i<N; i++) {
+ r = filtered.rows[i];
+ r.className = "show-row";
+ set_row_values(toroot, r, gMatches[i]);
+ set_row_selected(r, i == gSelectedIndex);
+ if (i == gSelectedIndex) {
+ gSelectedID = gMatches[i].id;
+ }
+ }
+ //start hiding rows that are no longer matches
+ for (; i<ROW_COUNT; i++) {
+ r = filtered.rows[i];
+ r.className = "no-display";
+ }
+ //if there are more results we're not showing, so say so.
+/* if (gMatches.length > ROW_COUNT) {
+ r = filtered.rows[ROW_COUNT];
+ r.className = "show-row";
+ c1 = r.cells[0];
+ c1.innerHTML = "plus " + (gMatches.length-ROW_COUNT) + " more";
+ } else {
+ filtered.rows[ROW_COUNT].className = "hide-row";
+ }*/
+ //if we have no results, hide the table
+ } else {
+ document.getElementById("search_filtered_div").className = "no-display";
+ }
+}
+
+function search_changed(e, kd, toroot)
+{
+ var search = document.getElementById("search_autocomplete");
+ var text = search.value;
+
+ // 13 = enter
+ if (!kd && (e.keyCode == 13)) {
+ document.getElementById("search_filtered_div").className = "no-display";
+ if (gSelectedIndex >= 0) {
+ window.location = toroot + gMatches[gSelectedIndex].link;
+ return false;
+ }
+ }
+ // 38 -- arrow up
+ else if (kd && (e.keyCode == 38)) {
+ if (gSelectedIndex >= 0) {
+ gSelectedIndex--;
+ }
+ sync_selection_table(toroot);
+ return false;
+ }
+ // 40 -- arrow down
+ else if (kd && (e.keyCode == 40)) {
+ if (gSelectedIndex < gMatches.length-1
+ && gSelectedIndex < ROW_COUNT-1) {
+ gSelectedIndex++;
+ }
+ sync_selection_table(toroot);
+ return false;
+ }
+ else if (!kd) {
+ gMatches = new Array();
+ matchedCount = 0;
+ gSelectedIndex = -1;
+ for (i=0; i<DATA.length; i++) {
+ var s = DATA[i];
+ if (text.length != 0 && s.label.indexOf(text) != -1) {
+ gMatches[matchedCount] = s;
+ if (gSelectedID == s.id) {
+ gSelectedIndex = matchedCount;
+ }
+ matchedCount++;
+ }
+ }
+ sync_selection_table(toroot);
+ return true; // allow the event to bubble up to the search api
+ }
+}
+
+function search_focus_changed(obj, focused)
+{
+ if (focused) {
+ if(obj.value == DEFAULT_TEXT){
+ obj.value = "";
+ obj.style.color="#000000";
+ }
+ } else {
+ if(obj.value == ""){
+ obj.value = DEFAULT_TEXT;
+ obj.style.color="#aaaaaa";
+ }
+ document.getElementById("search_filtered_div").className = "no-display";
+ }
+}
+
+function submit_search() {
+ var query = document.getElementById('search_autocomplete').value;
+ document.location = '/search.html#q=' + query;
+ return false;
+}
diff --git a/tools/droiddoc/templates/assets/style.css b/tools/droiddoc/templates/assets/style.css
new file mode 100644
index 000000000..5ad1118e5
--- /dev/null
+++ b/tools/droiddoc/templates/assets/style.css
@@ -0,0 +1,316 @@
+.jd-toptitle {
+ padding-left: 6px;
+ margin-bottom: 30px;
+ font-size: 160%;
+ font-weight: bold;
+}
+
+div#jd-content table {
+ border: none;
+}
+
+div#jd-content td, div#jd-content th {
+ font-size: small;
+}
+
+div#jd-content table.jd-linktable {
+ margin-top: 3px;
+ border-spacing: 0;
+}
+
+div#jd-content p.jd-deprecated-warning {
+ margin-top: 0;
+ margin-bottom: 10px;
+}
+
+div#jd-content table.jd-linktable th {
+ vertical-align: top;
+ text-align: left;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-left: 7px;
+ padding-right: 7px;
+ border: none;
+ border-top: 1px solid #d2d7d0;
+ background-color: #F7FCF4;
+}
+
+div#jd-content table.jd-linktable td {
+ border: none;
+}
+
+div#jd-content table.jd-linktable td p {
+ padding: 0;
+ margin: 0;
+ line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-linkcol {
+ vertical-align: top;
+ padding-top: 3px;
+ padding-bottom: 0;
+ padding-left: 7px;
+ padding-right: 7px;
+ border-top: 1px solid #d2d7d0;
+ background-color: #E5F1E0;
+ line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-descrcol {
+ vertical-align: top;
+ padding-top: 3px;
+ padding-bottom: 0;
+ padding-left: 7px;
+ padding-right: 7px;
+ border-top: 1px solid #d2d7d0;
+ background-color: #F7FCF4;
+ line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-descrcol p {
+ padding: 0;
+ margin: 0;
+ line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-valcol {
+ vertical-align: top;
+ padding-top: 3px;
+ padding-bottom: 0;
+ padding-left: 7px;
+ padding-right: 7px;
+ border-top: 1px solid #d2d7d0;
+ background-color: #E5F1E0;
+ line-height: 110%;
+}
+
+div#jd-content table.jd-linktable .jd-commentrow {
+ vertical-align: top;
+ padding-top: 3px;
+ padding-bottom: 4px;
+ padding-left: 7px;
+ padding-right: 7px;
+ background-color: #F7FCF4;
+ line-height: 110%;
+}
+
+div#jd-content div.jd-inheritedlinks {
+ vertical-align: top;
+ margin-top: 9px;
+ padding-left: 7px;
+ padding-right: 7px;
+ background-color: #F7FCF4;
+ line-height: 110%;
+}
+
+div#jd-content .jd-page_title-prefix {
+ padding-top: 2em;
+ margin-bottom: -14pt;
+}
+
+div#jd-content {
+ margin-left: 0;
+ margin-right: 10px;
+ margin-bottom: 0;
+}
+
+div#jd-content h1 {
+ padding-left: 10px;
+}
+
+div#jd-content h2 {
+ padding-left: 10px;
+}
+
+div#jd-content h4 {
+ margin-top: 9px;
+ margin-bottom: 1px;
+}
+
+div#jd-content .jd-descr h5 {
+ margin-bottom: 8px;
+}
+
+div#jd-content .sidebox h3 {
+ margin: 1em 0 0 0;
+}
+
+div#jd-content .jd-letterlist {
+ margin-top: 20px;
+ margin-bottom: 0;
+}
+
+div#jd-content .jd-lettertable {
+ margin-top: 15px;
+ margin-right: 10px;
+}
+div#jd-content .jd-letterentries {
+ list-style: none;
+ margin-left: 0;
+}
+div#jd-content .jd-letterentrycomments {
+ color: gray;
+}
+
+div#jd-content table.jd-inheritance-table {
+ margin-top: 0;
+ margin-left: 10px;
+ margin-right: 10px;
+ border-spacing: 0;
+}
+
+div#jd-content table.jd-inheritance-table td {
+ border: none;
+ margin: 0;
+ padding: 0;
+ background-color: white;
+}
+
+div#jd-content table.jd-inheritance-table .jd-inheritance-space {
+ width: 10px;
+}
+
+div#jd-content table.jd-inheritance-table .jd-inheritance-interface-cell {
+ padding-left: 17px;
+}
+
+div#jd-content h4.jd-details-title {
+ margin: 0;
+ background-color: #E5F1E0;
+ padding: 2px;
+ padding-left: 10px;
+ padding-right: 10px;
+ margin-top: 15px;
+}
+
+div#jd-content .jd-details {
+ margin-top: 0;
+ margin-left: -10px;
+}
+
+div#jd-content .jd-details-descr {
+ line-height: 120%;
+ padding-left: 10px;
+ padding-top: 10px;
+ padding-right: 20px;
+}
+
+div#jd-content .jd-descr h5,
+div#jd-content .jd-details h5 {
+ font-style: normal;
+ text-decoration: none;
+ font-size: 120%;
+}
+
+div#jd-content .jd-more {
+}
+
+div#jd-content .jd-descr {
+ padding-top: 0;
+}
+
+div#jd-content .jd-tagdata {
+ margin-top: 6px;
+ margin-bottom: 6px;
+}
+
+div#jd-content .jd-tagtitle {
+ margin-top: 0px;
+}
+
+div#jd-content .jd-tagtable {
+ margin-top: 10px;
+ border-spacing: 0;
+}
+
+div#jd-content .jd-tagtable th {
+ background: white;
+ padding-left: 10px;
+ padding-right: 10px;
+line-height: 120%;
+}
+
+div#jd-content .jd-tagtable th,
+div#jd-content .jd-tagtable td {
+line-height: 120%;
+ border: none;
+ margin: 0;
+ text-align: left;
+ padding-top: 0px;
+ padding-bottom: 5px;
+}
+
+div#jd-content .Code,code,pre,samp,var {
+ color: #004000;
+}
+
+div#jd-content pre.Code {
+ padding-left: 20px;
+}
+
+/* XXX I would really like to apply font-size: 9pt only if var/samp
+ is NOT inside of a .jd-descr div. */
+div#jd-content .jd-descr code,var,samp {
+ padding-left: 0px;
+}
+
+#search_autocomplete {
+ font-size: 80%;
+}
+
+div#jd-searchbox table.jd-autocomplete-table-hidden {
+ display: none;
+}
+
+div#jd-searchbox table.jd-autocomplete-table-showing {
+ z-index: 10;
+ border: 1px solid #3366cc;
+ position: relative;
+ top: -14px;
+ left: 5px;
+ background-color: white;
+}
+
+div#jd-searchbox td.jd-autocomplete {
+ font-family: Arial, sans-serif;
+ padding-left: 6px;
+ padding-right: 6px;
+ padding-top: 1px;
+ padding-bottom: 1px;
+ font-size: 80%;
+ border: none;
+ margin: 0;
+ line-height: 105%;
+}
+
+div#jd-searchbox td.jd-selected {
+ background-color: #E5F1E0;
+}
+
+div#jd-searchbox td.jd-linktype {
+ color: #999999;
+}
+
+div#jd-content .jd-expando-trigger {
+ margin-left: -8px;
+ margin-right: 0px;
+ border: none;
+}
+
+div#jd-build-id {
+ color: #666;
+ width: 100%;
+ text-align: right;
+ padding-right: 5px;
+ padding-bottom: 3px;
+}
+
+@media print {
+ #jd-searchbox, .jd-nav {
+ display: none;
+ }
+ div#jd-content {
+ margin-top: 0px;
+ }
+}
+
diff --git a/tools/droiddoc/templates/assets/triangle-none.gif b/tools/droiddoc/templates/assets/triangle-none.gif
new file mode 100644
index 000000000..0c7b469d8
--- /dev/null
+++ b/tools/droiddoc/templates/assets/triangle-none.gif
Binary files differ
diff --git a/tools/droiddoc/templates/class.cs b/tools/droiddoc/templates/class.cs
new file mode 100644
index 000000000..10778860c
--- /dev/null
+++ b/tools/droiddoc/templates/class.cs
@@ -0,0 +1,624 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body>
+<script type="text/javascript">
+function toggleInherited(linkObj, expand) {
+ var base = linkObj.getAttribute("id");
+ var list = document.getElementById(base + "-list");
+ var summary = document.getElementById(base + "-summary");
+ var trigger = document.getElementById(base + "-trigger");
+ var a = $(linkObj);
+ if ( (expand == null && a.hasClass("closed")) || expand ) {
+ list.style.display = "none";
+ summary.style.display = "block";
+ trigger.src = "<?cs var:toroot ?>assets/images/triangle-opened.png";
+ a.removeClass("closed");
+ a.addClass("opened");
+ } else if ( (expand == null && a.hasClass("opened")) || (expand == false) ) {
+ list.style.display = "block";
+ summary.style.display = "none";
+ trigger.src = "<?cs var:toroot ?>assets/images/triangle-closed.png";
+ a.removeClass("opened");
+ a.addClass("closed");
+ }
+ return false;
+}
+</script>
+
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="api-info-block">
+
+<?cs # are there inherited members ?>
+<?cs each:cl=class.inherited ?>
+ <?cs if:subcount(cl.methods) ?>
+ <?cs set:inhmethods = #1 ?>
+ <?cs /if ?>
+ <?cs if:subcount(cl.constants) ?>
+ <?cs set:inhconstants = #1 ?>
+ <?cs /if ?>
+ <?cs if:subcount(cl.fields) ?>
+ <?cs set:inhfields = #1 ?>
+ <?cs /if ?>
+ <?cs if:subcount(cl.attrs) ?>
+ <?cs set:inhattrs = #1 ?>
+ <?cs /if ?>
+<?cs /each ?>
+
+<div class="sum-details-links">
+Summary:
+<?cs if:subcount(class.inners) ?>
+ <a href="#nestedclasses">Nested Classes</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.attrs) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#lattrs">XML Attrs</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhattrs ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhattrs">Inherited XML Attrs</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.enumConstants) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#enumconstants">Enums</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.constants) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#constants">Constants</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhconstants ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhconstants">Inherited Constants</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.fields) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#lfields">Fields</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhfields ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhfields">Inherited Fields</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.ctors.public) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#pubctors">Ctors</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.ctors.protected) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#proctors">Protected Ctors</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.methods.public) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#pubmethods">Methods</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:subcount(class.methods.protected) ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#promethods">Protected Methods</a>
+ <?cs set:linkcount = #1 ?>
+<?cs /if ?>
+<?cs if:inhmethods ?>
+ <?cs if:linkcount ?>&#124; <?cs /if ?><a href="#inhmethods">Inherited Methods</a>
+<?cs /if ?>
+</nobr>
+<?cs if:inhattrs || inhconstants || inhfields || inhmethods || subcount(class.subclasses.direct) || subcount(class.subclasses.indirect) ?>
+&#124; <a href="#" onclick="return toggleAllSummaryInherited(this)">[Expand All]</a>
+<?cs /if ?>
+</div>
+</div>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== START OF CLASS DATA ======== -->
+
+<div id="jd-header">
+ <?cs var:class.scope ?>
+ <?cs var:class.static ?>
+ <?cs var:class.final ?>
+ <?cs var:class.abstract ?>
+ <?cs var:class.kind ?>
+<h1><?cs var:class.name ?></h1>
+
+<?cs set:colspan = subcount(class.inheritance) ?>
+<?cs each:supr = class.inheritance ?>
+ <?cs if:colspan == 2 ?>
+ extends <?cs call:type_link(supr.short_class) ?><br/>
+ <?cs /if ?>
+ <?cs if:last(supr) && subcount(supr.interfaces) ?>
+ implements
+ <?cs each:t=supr.interfaces ?>
+ <?cs call:type_link(t) ?>
+ <?cs /each ?>
+ <?cs /if ?>
+ <?cs set:colspan = colspan-1 ?>
+<?cs /each ?>
+
+</div><!-- end header -->
+
+
+<div id="jd-content">
+<table class="jd-inheritance-table">
+<?cs set:colspan = subcount(class.inheritance) ?>
+<?cs each:supr = class.inheritance ?>
+ <tr>
+ <?cs loop:i = 1, (subcount(class.inheritance)-colspan), 1 ?>
+ <td class="jd-inheritance-space">&nbsp;<?cs if:(subcount(class.inheritance)-colspan) == i ?>&nbsp;&nbsp;&#x21b3;<?cs /if ?></td>
+ <?cs /loop ?>
+ <td colspan="<?cs var:colspan ?>" class="jd-inheritance-class-cell"><?cs
+ if:colspan == 1
+ ?><?cs call:class_name(class.qualifiedType) ?><?cs
+ else
+ ?><?cs call:type_link(supr.class) ?><?cs
+ /if ?></td>
+ </tr>
+ <?cs set:colspan = colspan-1 ?>
+<?cs /each ?>
+</table>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+
+<?cs if:subcount(class.subclasses.direct) ?>
+<table class="jd-sumtable jd-sumtable-subclasses"><tr><td colspan="12" style="border:none;margin:0;padding:0;">
+<?cs call:expando_trigger("subclasses-direct", "closed") ?>Known Direct Subclasses
+<?cs call:expandable_class_list("subclasses-direct", class.subclasses.direct, "list") ?>
+</td></tr></table>
+<?cs /if ?>
+
+<?cs if:subcount(class.subclasses.indirect) ?>
+<table class="jd-sumtable jd-sumtable-subclasses"><tr><td colspan="12" style="border:none;margin:0;padding:0;">
+<?cs call:expando_trigger("subclasses-indirect", "closed") ?>Known Indirect Subclasses
+<?cs call:expandable_class_list("subclasses-indirect", class.subclasses.indirect, "list") ?>
+</td></tr></table>
+<?cs /if ?>
+
+<div class="jd-descr">
+<?cs call:deprecated_warning(class) ?>
+<?cs if:subcount(class.descr) ?>
+<h2>Class Overview</h2>
+<p><?cs call:tag_list(class.descr) ?></p>
+<?cs /if ?>
+
+<?cs call:see_also_tags(class.seeAlso) ?>
+
+</div><!-- jd-descr -->
+
+
+<?cs # summary macros ?>
+
+<?cs def:write_method_summary(methods) ?>
+<?cs set:count = #1 ?>
+<?cs each:method = methods ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-typecol"><nobr>
+ <?cs var:method.abstract ?>
+ <?cs var:method.synchronized ?>
+ <?cs var:method.final ?>
+ <?cs var:method.static ?>
+ <?cs call:type_link(method.generic) ?>
+ <?cs call:type_link(method.returnType) ?></nobr>
+ </td>
+ <td class="jd-linkcol" width="100%"><nobr>
+ <span class="sympad"><a href="<?cs var:toroot ?><?cs var:method.href ?>">
+ <?cs var:method.name ?></a></span>(<?cs call:parameter_list(method.params) ?>)</nobr>
+ <?cs if:subcount(method.shortDescr) || subcount(method.deprecated) ?>
+ <div class="jd-descrdiv"><?cs call:short_descr(method) ?></div>
+ <?cs /if ?>
+ </td></tr>
+<?cs set:count = count + #1 ?>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_field_summary(fields) ?>
+<?cs set:count = #1 ?>
+ <?cs each:field=fields ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-typecol"><nobr>
+ <?cs var:field.scope ?>
+ <?cs var:field.static ?>
+ <?cs var:field.final ?>
+ <?cs call:type_link(field.type) ?></nobr></td>
+ <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_constant_summary(fields) ?>
+<?cs set:count = #1 ?>
+ <?cs each:field=fields ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-typecol"><?cs call:type_link(field.type) ?></td>
+ <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a></td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?></td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_attr_summary(attrs) ?>
+<?cs set:count = #1 ?>
+ <tr>
+ <td><nobr><em>Attribute Name</em></nobr></td>
+ <td><nobr><em>Related Method</em></nobr></td>
+ <td><nobr><em>Description</em></nobr></td>
+ </tr>
+ <?cs each:attr=attrs ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></td>
+ <td class="jd-linkcol"><?cs each:m=attr.methods ?>
+ <a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a>
+ <?cs /each ?>
+ </td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(attr) ?>&nbsp;</td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_inners_summary(classes) ?>
+<?cs set:count = #1 ?>
+ <?cs each:cl=class.inners ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-typecol"><nobr>
+ <?cs var:class.scope ?>
+ <?cs var:class.static ?>
+ <?cs var:class.final ?>
+ <?cs var:class.abstract ?>
+ <?cs var:class.kind ?></nobr></td>
+ <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+<?cs /def ?>
+
+<?cs # end macros ?>
+
+<div class="jd-descr">
+<h2>Summary</h2>
+
+<?cs if:subcount(class.inners) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== NESTED CLASS SUMMARY ======== -->
+<table id="nestedclasses" class="jd-sumtable"><tr><th colspan="12">Nested Classes</th></tr>
+<?cs call:write_inners_summary(class.inners) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<?cs if:subcount(class.attrs) ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="lattrs" class="jd-sumtable"><tr><th colspan="12">XML Attributes</th></tr>
+<?cs call:write_attr_summary(class.attrs) ?>
+<?cs /if ?>
+
+<?cs # if there are inherited attrs, write the table ?>
+<?cs if:inhattrs ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="inhattrs" class="jd-sumtable"><tr><th>
+ <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+ <div style="clear:left;">Inherited XML Attributes</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.attrs) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-attrs-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-attrs-<?cs var:cl.qualified ?>">
+ <div id="inherited-attrs-<?cs var:cl.qualified ?>-list"
+ class="jd-inheritedlinks">
+ </div>
+ <div id="inherited-attrs-<?cs var:cl.qualified ?>-summary" style="display: none;">
+ <table class="jd-sumtable-expando">
+ <?cs call:write_attr_summary(cl.attrs) ?></table>
+ </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.enumConstants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="enumconstants" class="jd-sumtable"><tr><th colspan="12">Enum Values</th></tr>
+<?cs set:count = #1 ?>
+ <?cs each:field=class.enumConstants ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-descrcol"><?cs call:type_link(field.type) ?>&nbsp;</td>
+ <td class="jd-linkcol"><a href="<?cs var:toroot ?><?cs var:field.href ?>"><?cs var:field.name ?></a>&nbsp;</td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(field) ?>&nbsp;</td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+<?cs /if ?>
+
+<?cs if:subcount(class.constants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
+<?cs call:write_constant_summary(class.constants) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited constants, write the table ?>
+<?cs if:inhconstants ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== ENUM CONSTANT SUMMARY =========== -->
+<table id="inhconstants" class="jd-sumtable"><tr><th>
+ <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+ <div style="clear:left;">Inherited Constants</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.constants) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-constants-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-constants-<?cs var:cl.qualified ?>">
+ <div id="inherited-constants-<?cs var:cl.qualified ?>-list"
+ class="jd-inheritedlinks">
+ </div>
+ <div id="inherited-constants-<?cs var:cl.qualified ?>-summary" style="display: none;">
+ <table class="jd-sumtable-expando">
+ <?cs call:write_constant_summary(cl.constants) ?></table>
+ </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.fields) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="lfields" class="jd-sumtable"><tr><th colspan="12">Fields</th></tr>
+<?cs call:write_field_summary(class.fields) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited fields, write the table ?>
+<?cs if:inhfields ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- =========== FIELD SUMMARY =========== -->
+<table id="inhfields" class="jd-sumtable"><tr><th>
+ <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+ <div style="clear:left;">Inherited Fields</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.fields) ?>
+<tr><td colspan="12">
+<?cs call:expando_trigger("inherited-fields-"+cl.qualified, "closed") ?>From <?cs var:cl.kind ?>
+<a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-fields-<?cs var:cl.qualified ?>">
+ <div id="inherited-fields-<?cs var:cl.qualified ?>-list"
+ class="jd-inheritedlinks">
+ </div>
+ <div id="inherited-fields-<?cs var:cl.qualified ?>-summary" style="display: none;">
+ <table class="jd-sumtable-expando">
+ <?cs call:write_field_summary(cl.fields) ?></table>
+ </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.ctors.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+<table id="pubctors" class="jd-sumtable"><tr><th colspan="12">Public Constructors</th></tr>
+<?cs call:write_method_summary(class.ctors.public) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.ctors.protected) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ======== CONSTRUCTOR SUMMARY ======== -->
+<table id="proctors" class="jd-sumtable"><tr><th colspan="12">Protected Constructors</th></tr>
+<?cs call:write_method_summary(class.ctors.protected) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.methods.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="pubmethods" class="jd-sumtable"><tr><th colspan="12">Public Methods</th></tr>
+<?cs call:write_method_summary(class.methods.public) ?>
+</table>
+<?cs /if ?>
+
+<?cs if:subcount(class.methods.protected) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="promethods" class="jd-sumtable"><tr><th colspan="12">Protected Methods</th></tr>
+<?cs call:write_method_summary(class.methods.protected) ?>
+</table>
+<?cs /if ?>
+
+<?cs # if there are inherited methods, write the table ?>
+<?cs if:inhmethods ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========== METHOD SUMMARY =========== -->
+<table id="inhmethods" class="jd-sumtable"><tr><th>
+ <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
+ <div style="clear:left;">Inherited Methods</div></th></tr>
+<?cs each:cl=class.inherited ?>
+<?cs if:subcount(cl.methods) ?>
+<tr><td colspan="12"><?cs call:expando_trigger("inherited-methods-"+cl.qualified, "closed") ?>
+From <?cs var:cl.kind ?> <a href="<?cs var:toroot ?><?cs var:cl.link ?>"><?cs var:cl.qualified ?></a>
+<div id="inherited-methods-<?cs var:cl.qualified ?>">
+ <div id="inherited-methods-<?cs var:cl.qualified ?>-list"
+ class="jd-inheritedlinks">
+ </div>
+ <div id="inherited-methods-<?cs var:cl.qualified ?>-summary" style="display: none;">
+ <table class="jd-sumtable-expando">
+ <?cs call:write_method_summary(cl.methods) ?></table>
+ </div>
+</div>
+</td></tr>
+<?cs /if ?>
+<?cs /each ?>
+</table>
+<?cs /if ?>
+
+</div><!-- jd-descr (summary) -->
+
+<!-- Details -->
+
+<?cs def:write_field_details(fields) ?>
+<?cs each:field=fields ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:field.anchor ?>"></A>
+<div class="jd-details">
+ <h4 class="jd-details-title">
+ <span class="normal">
+ <?cs var:field.scope ?>
+ <?cs var:field.static ?>
+ <?cs var:field.final ?>
+ <?cs call:type_link(field.type) ?>
+ </span>
+ <?cs var:field.name ?>
+ </h4>
+ <div class="jd-details-descr"><?cs call:description(field) ?>
+ <?cs if:subcount(field.constantValue) ?>
+ <div class="jd-tagdata">
+ <span class="jd-tagtitle">Constant Value: </span>
+ <span>
+ <?cs if:field.constantValue.isString ?>
+ <?cs var:field.constantValue.str ?>
+ <?cs else ?>
+ <?cs var:field.constantValue.dec ?>
+ (<?cs var:field.constantValue.hex ?>)
+ <?cs /if ?>
+ </span>
+ </div>
+ <?cs /if ?>
+ </div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_method_details(methods) ?>
+<?cs each:method=methods ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:method.anchor ?>"></A>
+<div class="jd-details">
+ <h4 class="jd-details-title">
+ <span class="normal">
+ <?cs var:method.scope ?>
+ <?cs var:method.static ?>
+ <?cs var:method.final ?>
+ <?cs var:method.abstract ?>
+ <?cs var:method.synchronized ?>
+ <?cs call:type_link(method.returnType) ?>
+ </span>
+ <span class="sympad"><?cs var:method.name ?></span>
+ <span class="normal">(<?cs call:parameter_list(method.params) ?>)</span>
+ </h4>
+ <div class="jd-details-descr"><?cs call:description(method) ?></div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+<?cs def:write_attr_details(attrs) ?>
+<?cs each:attr=attrs ?>
+<?cs # the A tag in the next line must remain where it is, so that Eclipse can parse the docs ?>
+<A NAME="<?cs var:attr.anchor ?>"></A>
+<div class="jd-details">
+ <h4 class="jd-details-title"><?cs var:attr.name ?></h4>
+ <div class="jd-details-descr">
+ <?cs call:description(attr) ?>
+
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Related Methods</h5>
+ <ul class="nolist">
+ <?cs each:m=attr.methods ?>
+ <li><a href="<?cs var:toroot ?><?cs var:m.href ?>"><?cs var:m.name ?></a></li>
+ <?cs /each ?>
+ </ul>
+ </div>
+ </div>
+</div>
+<?cs /each ?>
+<?cs /def ?>
+
+
+<!-- XML Attributes -->
+<?cs if:subcount(class.attrs) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= FIELD DETAIL ======== -->
+<h2>XML Attributes</h2>
+<?cs call:write_attr_details(class.attrs) ?>
+<?cs /if ?>
+
+<!-- Enum Values -->
+<?cs if:subcount(class.enumConstants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= ENUM CONSTANTS DETAIL ======== -->
+<h2>Enum Values</h2>
+<?cs call:write_field_details(class.enumConstants) ?>
+<?cs /if ?>
+
+<!-- Constants -->
+<?cs if:subcount(class.constants) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= ENUM CONSTANTS DETAIL ======== -->
+<h2>Constants</h2>
+<?cs call:write_field_details(class.constants) ?>
+<?cs /if ?>
+
+<!-- Fields -->
+<?cs if:subcount(class.fields) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= FIELD DETAIL ======== -->
+<h2>Fields</h2>
+<?cs call:write_field_details(class.fields) ?>
+<?cs /if ?>
+
+<!-- Public ctors -->
+<?cs if:subcount(class.ctors.public) ?>
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+<h2>Public Constructors</h2>
+<?cs call:write_method_details(class.ctors.public) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= CONSTRUCTOR DETAIL ======== -->
+<!-- Protected ctors -->
+<?cs if:subcount(class.ctors.protected) ?>
+<h2>Protected Constructors</h2>
+<?cs call:write_method_details(class.ctors.protected) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= METHOD DETAIL ======== -->
+<!-- Public methdos -->
+<?cs if:subcount(class.methods.public) ?>
+<h2>Public Methods</h2>
+<?cs call:write_method_details(class.methods.public) ?>
+<?cs /if ?>
+
+<?cs # this next line must be exactly like this to be parsed by eclipse ?>
+<!-- ========= METHOD DETAIL ======== -->
+<?cs if:subcount(class.methods.protected) ?>
+<h2>Protected Methods</h2>
+<?cs call:write_method_details(class.methods.protected) ?>
+<?cs /if ?>
+
+<?cs # the next two lines must be exactly like this to be parsed by eclipse ?>
+<!-- ========= END OF CLASS DATA ========= -->
+<A NAME="navbar_top"></A>
+
+<?cs include:"footer.cs" ?>
+</div> <!-- jd-content -->
+
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/classes.cs b/tools/droiddoc/templates/classes.cs
new file mode 100644
index 000000000..abe8e4e0f
--- /dev/null
+++ b/tools/droiddoc/templates/classes.cs
@@ -0,0 +1,41 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-letterlist"><?cs each:letter=docs.classes ?>
+ <a href="#letter_<?cs name:letter ?>"><?cs name:letter ?></a><?cs /each?>
+</div>
+
+<?cs each:letter=docs.classes ?>
+<?cs set:count = #1 ?>
+<h2 id="letter_<?cs name:letter ?>"><?cs name:letter ?></h2>
+<table class="jd-sumtable">
+ <?cs set:cur_row = #0 ?>
+ <?cs each:cl = letter ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+ </tr>
+ <?cs set:count = count + #1 ?>
+ <?cs /each ?>
+</table>
+<?cs /each ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html> \ No newline at end of file
diff --git a/tools/droiddoc/templates/customization.cs b/tools/droiddoc/templates/customization.cs
new file mode 100644
index 000000000..3646495a6
--- /dev/null
+++ b/tools/droiddoc/templates/customization.cs
@@ -0,0 +1,24 @@
+<?cs # This default template file is meant to be replaced. ?>
+<?cs # Use the -templatedir arg to javadoc to set your own directory with a ?>
+<?cs # replacement for this file in it. ?>
+
+<?cs # appears at the top of every page ?><?cs
+def:custom_masthead() ?>
+ <div id="header">
+ <div id="headerLeft">
+ <a href="<?cs var:toroot ?>index.html" tabindex="-1"><?cs var:page.title ?></a>
+ </div>
+ <div id="headerRight">
+ <?cs call:default_search_box() ?>
+ </div><!-- headerRight -->
+ </div><!-- header --><?cs
+/def ?>
+
+<?cs # appear at the bottom of every page ?>
+<?cs def:custom_copyright() ?><?cs /def ?>
+<?cs def:custom_cc_copyright() ?><?cs /def ?>
+<?cs def:custom_footerlinks() ?><?cs /def ?>
+<?cs def:custom_buildinfo() ?>Build <?cs var:page.build ?> - <?cs var:page.now ?><?cs /def ?>
+
+<?cs # appears on the side of the page ?>
+<?cs def:custom_left_nav() ?><?cs call:default_left_nav() ?><?cs /def ?>
diff --git a/tools/droiddoc/templates/docpage.cs b/tools/droiddoc/templates/docpage.cs
new file mode 100644
index 000000000..06b3f3598
--- /dev/null
+++ b/tools/droiddoc/templates/docpage.cs
@@ -0,0 +1,42 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+ <span class="crumb">
+ <?cs if:parent.link ?>
+ <a href="<?cs var:parent.link ?>"><?cs var:parent.title ?></a> >
+ <?cs else ?>&nbsp;
+ <?cs /if ?>
+ </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+ <div id="jd-content">
+
+
+ <div class="jd-descr">
+ <?cs call:tag_list(root.descr) ?>
+ </div>
+
+ <a href="#top" style="float:right">&uarr; Go to top</a>
+ <?cs if:parent.link ?>
+ <p><a href="<?cs var:parent.link ?>">&larr; Back to <?cs var:parent.title ?></a></p>
+ <?cs /if ?>
+ </div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/doctype.cs b/tools/droiddoc/templates/doctype.cs
new file mode 100644
index 000000000..643f99270
--- /dev/null
+++ b/tools/droiddoc/templates/doctype.cs
@@ -0,0 +1 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> \ No newline at end of file
diff --git a/tools/droiddoc/templates/footer.cs b/tools/droiddoc/templates/footer.cs
new file mode 100644
index 000000000..bb82c8d26
--- /dev/null
+++ b/tools/droiddoc/templates/footer.cs
@@ -0,0 +1,19 @@
+<div id="footer">
+
+<?cs if:reference||guide ?>
+ <div id="copyright">
+ <?cs call:custom_copyright() ?>
+ </div>
+ <div id="build_info">
+ <?cs call:custom_buildinfo() ?>
+ </div>
+<?cs elif:!hide_license_footer ?>
+ <div id="copyright">
+ <?cs call:custom_cc_copyright() ?>
+ </div>
+<?cs /if ?>
+ <div id="footerlinks">
+ <?cs call:custom_footerlinks() ?>
+ </div>
+
+</div> <!-- end footer -->
diff --git a/tools/droiddoc/templates/head_tag.cs b/tools/droiddoc/templates/head_tag.cs
new file mode 100644
index 000000000..55d022512
--- /dev/null
+++ b/tools/droiddoc/templates/head_tag.cs
@@ -0,0 +1,37 @@
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<link rel="shortcut icon" type="image/x-icon" href="<?cs var:toroot ?>favicon.ico" />
+<title><?cs
+ if:page.title ?><?cs
+ var:page.title ?><?cs
+ if:sdk.version ?> (<?cs
+ var:sdk.version ?>)<?cs
+ /if ?> | <?cs
+ /if ?>Android Developers</title><?cs
+if:guide||sdk ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs-devguide.css" rel="stylesheet" type="text/css" /><?cs
+else ?>
+<link href="<?cs var:toroot ?>assets/android-developer-docs.css" rel="stylesheet" type="text/css" /><?cs
+/if ?>
+<script src="<?cs var:toroot ?>assets/search_autocomplete.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>reference/lists.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/jquery-resizable.min.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/android-developer-docs.js" type="text/javascript"></script>
+<script type="text/javascript">
+ setToRoot("<?cs var:toroot ?>");
+</script><?cs
+if:reference ?>
+<script src="<?cs var:toroot ?>navtree_data.js" type="text/javascript"></script>
+<script src="<?cs var:toroot ?>assets/navtree.js" type="text/javascript"></script><?cs
+/if ?>
+<noscript>
+ <style type="text/css">
+ body{overflow:auto;}
+ #body-content{position:relative; top:0;}
+ #doc-content{overflow:visible;border-left:3px solid #666;}
+ #side-nav{padding:0;}
+ #side-nav .toggle-list ul {display:block;}
+ #resize-packages-nav{border-bottom:3px solid #666;}
+ </style>
+</noscript>
+</head>
diff --git a/tools/droiddoc/templates/header.cs b/tools/droiddoc/templates/header.cs
new file mode 100644
index 000000000..e8301bed9
--- /dev/null
+++ b/tools/droiddoc/templates/header.cs
@@ -0,0 +1,3 @@
+<?cs call:custom_masthead() ?>
+<?cs call:custom_left_nav() ?>
+
diff --git a/tools/droiddoc/templates/hierarchy.cs b/tools/droiddoc/templates/hierarchy.cs
new file mode 100644
index 000000000..a607ffd3d
--- /dev/null
+++ b/tools/droiddoc/templates/hierarchy.cs
@@ -0,0 +1,68 @@
+<?cs include:"macros.cs" ?>
+<html>
+<style>
+ .jd-hierarchy-spacer {
+ width: 15px;
+ }
+ .jd-hierarchy-data {
+ text-align: left;
+ vertical-align: top;
+ }
+</style>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div style="margin-left: 20px; margin-right: 20px;">
+
+<?cs def:hierarchy_list(classes) ?>
+<?cs each:cl = classes ?>
+<tr>
+ <?cs loop:x=#0,cl.indent,#1 ?><td class="jd-hierarchy-spacer"></td><?cs /loop ?>
+ <td class="jd-hierarchy-data" colspan="<?cs var:cl.colspan ?>">
+ <?cs if:cl.exists ?>
+ <?cs call:type_link(cl.class) ?>
+ <?cs else ?>
+ <?cs var:cl.value ?>
+ <?cs /if ?>
+ </td>
+ <td class="jd-hierarchy-data">
+ <?cs each:iface = cl.interfaces ?>
+ <?cs if:iface.exists ?>
+ <?cs call:type_link(iface.class) ?>
+ <?cs else ?>
+ <?cs var:iface.value ?>
+ <?cs /if ?> &nbsp;&nbsp;
+ <?cs /each ?>
+ &nbsp;
+ </td>
+</tr>
+<?cs call:hierarchy_list(cl.derived) ?>
+<?cs /each ?>
+<?cs /def ?>
+
+
+<table border="0" cellpadding="0" cellspacing="1">
+<th class="jd-hierarchy-data" colspan="<?cs var:colspan ?>">Class</th>
+<th class="jd-hierarchy-data">Interfaces</th>
+<?cs call:hierarchy_list(classes) ?>
+</table>
+
+</div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
diff --git a/tools/droiddoc/templates/index.cs b/tools/droiddoc/templates/index.cs
new file mode 100644
index 000000000..15a6a590f
--- /dev/null
+++ b/tools/droiddoc/templates/index.cs
@@ -0,0 +1,8 @@
+<html>
+<head>
+<meta http-equiv="refresh" content="0;url=packages.html">
+</head>
+<body>
+<?cs include:"analytics.cs" ?>
+</body>
+</html> \ No newline at end of file
diff --git a/tools/droiddoc/templates/keywords.cs b/tools/droiddoc/templates/keywords.cs
new file mode 100644
index 000000000..0c8d4e3e8
--- /dev/null
+++ b/tools/droiddoc/templates/keywords.cs
@@ -0,0 +1,37 @@
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-letterlist"><?cs each:letter=keywords ?>
+ <a href="#letter_<?cs name:letter ?>"><?cs name:letter ?></a><?cs /each?>
+</div>
+
+<?cs each:letter=keywords ?>
+<a name="letter_<?cs name:letter ?>"></a>
+<h2><?cs name:letter ?></h2>
+<ul class="jd-letterentries">
+<?cs each:entry=letter
+?> <li><a href="<?cs var:toroot ?><?cs var:entry.href ?>"><?cs var:entry.label
+ ?></a>&nbsp;<font class="jd-letterentrycomments">(<?cs var:entry.comment ?>)</font></li>
+<?cs /each
+?></ul>
+
+<?cs /each ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/lists.cs b/tools/droiddoc/templates/lists.cs
new file mode 100644
index 000000000..0af32b283
--- /dev/null
+++ b/tools/droiddoc/templates/lists.cs
@@ -0,0 +1,5 @@
+var DATA = [
+<?cs each:page = docs.pages
+?> { id:<?cs var: page.id ?>, label:"<?cs var:page.label ?>", link:"<?cs var:page.link ?>", type:"<?cs var:page.type ?>" }<?cs if:!last(page) ?>,<?cs /if ?>
+<?cs /each ?>
+ ];
diff --git a/tools/droiddoc/templates/macros.cs b/tools/droiddoc/templates/macros.cs
new file mode 100644
index 000000000..fe890453d
--- /dev/null
+++ b/tools/droiddoc/templates/macros.cs
@@ -0,0 +1,333 @@
+<?cs # A link to a package ?><?cs
+def:package_link(pkg)) ?>
+ <a href="<?cs var:toroot ?><?cs var:pkg.link ?>"><?cs var:pkg.name ?></a><?cs
+/def ?>
+
+<?cs # A link to a type, or not if it's a primitive type
+ link: whether to create a link at the top level, always creates links in
+ recursive invocations.
+ Expects the following fields:
+ .name
+ .link
+ .isPrimitive
+ .superBounds.N.(more links) (... super ... & ...)
+ .extendsBounds.N.(more links) (... extends ... & ...)
+ .typeArguments.N.(more links) (< ... >)
+?><?cs
+def:type_link_impl(type, link) ?><?cs
+ if:type.link && link=="true" ?><a href="<?cs var:toroot ?><?cs var:type.link ?>"><?cs /if
+ ?><?cs var:type.label ?><?cs if:type.link && link=="true" ?></a><?cs /if ?><?cs
+ if:subcount(type.extendsBounds) ?><?cs
+ each:t=type.extendsBounds ?><?cs
+ if:first(t) ?>&nbsp;extends&nbsp;<?cs else ?>&nbsp;&amp;&nbsp;<?cs /if ?><?cs
+ call:type_link_impl(t, "true") ?><?cs
+ /each ?><?cs
+ /if ?><?cs
+ if:subcount(type.superBounds) ?><?cs
+ each:t=type.superBounds ?><?cs
+ if:first(t) ?>&nbsp;super&nbsp;<?cs else ?>&nbsp;&amp;&nbsp;<?cs /if ?><?cs
+ call:type_link_impl(t, "true") ?><?cs
+ /each ?><?cs
+ /if ?><?cs
+ if:subcount(type.typeArguments)
+ ?>&lt;<?cs each:t=type.typeArguments ?><?cs call:type_link_impl(t, "true") ?><?cs
+ if:!last(t) ?>,&nbsp;<?cs /if ?><?cs
+ /each ?>&gt;<?cs
+ /if ?><?cs
+/def ?>
+
+<?cs def:class_name(type) ?><?cs call:type_link_impl(type, "false") ?><?cs /def ?>
+<?cs def:type_link(type) ?><?cs call:type_link_impl(type, "true") ?><?cs /def ?>
+
+<?cs # A comma separated parameter list ?><?cs
+def:parameter_list(params) ?><?cs
+ each:param = params ?><?cs
+ call:type_link(param.type)?> <?cs
+ var:param.name ?><?cs
+ if: name(param)!=subcount(params)-1?>, <?cs /if ?><?cs
+ /each ?><?cs
+/def ?>
+
+<?cs # Print a list of tags (e.g. description text ?><?cs
+def:tag_list(tags) ?><?cs
+ each:tag = tags ?><?cs
+ if:tag.name == "Text" ?><?cs var:tag.text?><?cs
+ elif:tag.kind == "@more" ?><p><?cs
+ elif:tag.kind == "@see" ?><a href="<?cs var:toroot ?><?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+ elif:tag.kind == "@seeHref" ?><a href="<?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+ elif:tag.kind == "@seeJustLabel" ?><?cs var:tag.label ?><?cs
+ elif:tag.kind == "@code" ?><code class="Code prettyprint"><?cs var:tag.text ?></code><?cs
+ elif:tag.kind == "@samplecode" ?><pre class="Code prettyprint"><?cs var:tag.text ?></pre><?cs
+ elif:tag.name == "@sample" ?><pre class="Code prettyprint"><?cs var:tag.text ?></pre><?cs
+ elif:tag.name == "@include" ?><?cs var:tag.text ?><?cs
+ elif:tag.kind == "@docRoot" ?><?cs var:toroot ?><?cs
+ elif:tag.kind == "@inheritDoc" ?><?cs # This is the case when @inheritDoc is in something
+ that doesn't inherit from anything?><?cs
+ elif:tag.kind == "@attr" ?><?cs
+ else ?>{<?cs var:tag.name?> <?cs var:tag.text ?>}<?cs
+ /if ?><?cs
+ /each ?><?cs
+/def ?>
+
+<?cs # The message about This xxx is deprecated. ?><?cs
+def:deprecated_text(kind) ?>
+ This <?cs var:kind ?> is deprecated.<?cs
+/def ?>
+
+<?cs # Show the short-form description of something. These come from shortDescr and deprecated ?><?cs
+def:short_descr(obj) ?><?cs
+ if:subcount(obj.deprecated) ?>
+ <em><?cs call:deprecated_text(obj.kind) ?>
+ <?cs call:tag_list(obj.deprecated) ?></em><?cs
+ else ?><?cs call:tag_list(obj.shortDescr) ?><?cs
+ /if ?><?cs
+/def ?>
+
+<?cs # Show the red box with the deprecated warning ?><?cs
+def:deprecated_warning(obj) ?><?cs
+ if:subcount(obj.deprecated) ?><p>
+ <p class="warning jd-deprecated-warning">
+ <strong><?cs call:deprecated_text(obj.kind) ?></strong><?cs
+ call:tag_list(obj.deprecated) ?>
+ </p><?cs
+ /if ?><?cs
+/def ?>
+
+<?cs # print the See Also: section ?><?cs
+def:see_also_tags(also) ?><?cs
+ if:subcount(also) ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">See Also</h5>
+ <ul class="nolist"><?cs
+ each:tag=also ?><li><?cs
+ if:tag.kind == "@see" ?><a href="<?cs var:toroot ?><?cs var:tag.href ?>"><?cs
+ var:tag.label ?></a><?cs
+ elif:tag.kind == "@seeHref" ?><a href="<?cs var:tag.href ?>"><?cs var:tag.label ?></a><?cs
+ elif:tag.kind == "@seeJustLabel" ?><?cs var:tag.label ?><?cs
+ else ?>[ERROR: Unknown @see kind]<?cs
+ /if ?></li><?cs
+ /each ?>
+ </ul>
+ </div><?cs
+ /if ?>
+<?cs /def ?>
+
+
+<?cs # Print the long-form description for something.
+ Uses the following fields: deprecated descr seeAlso ?><?cs
+def:description(obj) ?><?cs
+ call:deprecated_warning(obj) ?>
+ <div class="jd-tagdata jd-tagdescr"><p><?cs call:tag_list(obj.descr) ?></p></div><?cs
+ if:subcount(obj.attrRefs) ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Related XML Attributes</h5>
+ <ul class="nolist"><?cs
+ each:attr=obj.attrRefs ?>
+ <li><a href="<?cs var:toroot ?><?cs var:attr.href ?>"><?cs var:attr.name ?></a></li><?cs
+ /each ?>
+ </ul>
+ </div><?cs
+ /if ?><?cs
+ if:subcount(obj.paramTags) ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Parameters</h5>
+ <table class="jd-tagtable"><?cs
+ each:tag=obj.paramTags ?>
+ <tr>
+ <th><?cs if:tag.isTypeParameter ?>&lt;<?cs /if ?><?cs var:tag.name
+ ?><?cs if:tag.isTypeParameter ?>&gt;<?cs /if ?></td>
+ <td><?cs call:tag_list(tag.comment) ?></td>
+ </tr><?cs
+ /each ?>
+ </table>
+ </div><?cs
+ /if ?><?cs
+ if:subcount(obj.returns) ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Returns</h5>
+ <ul class="nolist"><li><?cs call:tag_list(obj.returns) ?></li></ul>
+ </div><?cs
+ /if ?><?cs
+ if:subcount(obj.throws) ?>
+ <div class="jd-tagdata">
+ <h5 class="jd-tagtitle">Throws</h5>
+ <table class="jd-tagtable"><?cs
+ each:tag=obj.throws ?>
+ <tr>
+ <th><?cs call:type_link(tag.type) ?></td>
+ <td><?cs call:tag_list(tag.comment) ?></td>
+ </tr><?cs
+ /each ?>
+ </table>
+ </div><?cs
+ /if ?><?cs
+ call:see_also_tags(obj.seeAlso) ?><?cs
+/def ?>
+
+<?cs # A table of links to classes with descriptions, as in a package file or the nested classes ?><?cs
+def:class_link_table(classes) ?><?cs
+ set:count = #1 ?>
+ <table class="jd-sumtable-expando"><?cs
+ each:cl=classes ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-linkcol"><?cs call:type_link(cl.type) ?></td>
+ <td class="jd-descrcol" width="100%"><?cs call:short_descr(cl) ?>&nbsp;</td>
+ </tr><?cs set:count = count + #1 ?><?cs
+ /each ?>
+ </table><?cs
+/def ?>
+
+<?cs # A list of links to classes, for use in the side navigation of packages ?><?cs
+def:class_link_list(label, classes) ?><?cs
+ if:subcount(classes) ?>
+ <li><h2><?cs var:label ?></h2>
+ <ul><?cs
+ each:cl=classes ?>
+ <li><?cs call:type_link(cl.type) ?></li><?cs
+ /each ?>
+ </ul>
+ </li><?cs
+ /if ?><?cs
+/def ?>
+
+<?cs # A list of links to classes, for use in the side navigation of classes ?><?cs
+def:list(label, classes) ?><?cs
+ if:subcount(classes) ?>
+ <li><h2><?cs var:label ?></h2>
+ <ul><?cs
+ each:cl=classes ?>
+ <li <?cs if:class.name == cl.label?>class="selected"<?cs /if ?>><?cs call:type_link(cl) ?></li><?cs
+ /each ?>
+ </ul>
+ </li><?cs
+ /if ?><?cs
+/def ?>
+
+<?cs # An expando trigger ?><?cs
+def:expando_trigger(id, default) ?>
+ <a href="#" onclick="return toggleInherited(this, null)" id="<?cs var:id ?>" class="jd-expando-trigger closed"
+ ><img id="<?cs var:id ?>-trigger"
+ src="<?cs var:toroot ?>assets/images/triangle-<?cs var:default ?>.png"
+ class="jd-expando-trigger-img" /></a><?cs
+/def ?>
+
+<?cs # An expandable list of classes ?><?cs
+def:expandable_class_list(id, classes, default) ?>
+ <div id="<?cs var:id ?>">
+ <div id="<?cs var:id ?>-list"
+ class="jd-inheritedlinks"
+ <?cs if:default != "list" ?>style="display: none;"<?cs /if ?>
+ ><?cs
+ each:cl=classes ?>
+ <?cs call:type_link(cl.type) ?><?cs if:!last(cl) ?>,<?cs /if ?><?cs
+ /each ?>
+ </div>
+ <div id="<?cs var:id ?>-summary"
+ <?cs if:default != "summary" ?>style="display: none;"<?cs /if ?>
+ ><?cs
+ call:class_link_table(classes) ?>
+ </div>
+ </div><?cs
+/def ?>
+
+<?cs # The default side navigation for the reference docs ?><?cs
+def:default_left_nav() ?>
+ <div class="g-section g-tpl-240" id="body-content">
+ <div class="g-unit g-first side-nav-resizable" id="side-nav">
+ <div id="swapper">
+ <div id="nav-panels">
+ <div id="resize-packages-nav">
+ <div id="packages-nav">
+ <div id="index-links"><nobr>
+ <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> |
+ <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+ </div>
+ <ul><?cs
+ each:pkg=docs.packages ?>
+ <li <?cs if:(class.package.name == pkg.name) || (package.name == pkg.name)?>class="selected"<?cs /if ?>><?cs call:package_link(pkg) ?></li><?cs
+ /each ?>
+ </ul><br/>
+ </div> <!-- end packages -->
+ </div> <!-- end resize-packages -->
+ <div id="classes-nav"><?cs
+ if:subcount(class.package) ?>
+ <ul>
+ <?cs call:list("Interfaces", class.package.interfaces) ?>
+ <?cs call:list("Classes", class.package.classes) ?>
+ <?cs call:list("Enums", class.package.enums) ?>
+ <?cs call:list("Exceptions", class.package.exceptions) ?>
+ <?cs call:list("Errors", class.package.errors) ?>
+ </ul><?cs
+ elif:subcount(package) ?>
+ <ul>
+ <?cs call:class_link_list("Interfaces", package.interfaces) ?>
+ <?cs call:class_link_list("Classes", package.classes) ?>
+ <?cs call:class_link_list("Enums", package.enums) ?>
+ <?cs call:class_link_list("Exceptions", package.exceptions) ?>
+ <?cs call:class_link_list("Errors", package.errors) ?>
+ </ul><?cs
+ else ?>
+ <script>
+ /*addLoadEvent(maxPackageHeight);*/
+ </script>
+ <p style="padding:10px">Select a package to view its members</p><?cs
+ /if ?><br/>
+ </div><!-- end classes -->
+ </div><!-- end nav-panels -->
+ <div id="nav-tree" style="display:none">
+ <div id="index-links"><nobr>
+ <a href="<?cs var:toroot ?>reference/packages.html" <?cs if:(page.title == "Package Index") ?>class="selected"<?cs /if ?> >Package Index</a> |
+ <a href="<?cs var:toroot ?>reference/classes.html" <?cs if:(page.title == "Class Index") ?>class="selected"<?cs /if ?>>Class Index</a></nobr>
+ </div>
+ </div><!-- end nav-tree -->
+ </div><!-- end swapper -->
+ </div> <!-- end side-nav -->
+ <script>
+ $("<a href='#' id='nav-swap' onclick='swapNav();return false;' style='font-size:10px;line-height:9px;margin-left:1em;text-decoration:none;'><span id='tree-link'>Use Tree Navigation</span><span id='panel-link' style='display:none'>Use Panel Navigation</span></a>").appendTo("#side-nav");
+ chooseDefaultNav();
+ if ($("#nav-tree").is(':visible')) init_navtree("nav-tree", "<?cs var:toroot ?>", NAVTREE_DATA);
+ else {
+ addLoadEvent(function() {
+ scrollIntoView("packages-nav");
+ scrollIntoView("classes-nav");
+ });
+ }
+ $("#swapper").css({borderBottom:"2px solid #aaa"});
+ </script><?cs
+/def ?>
+
+<?cs # The default search box that goes in the header ?><?cs
+def:default_search_box() ?>
+ <div id="search" >
+ <div id="searchForm">
+ <form accept-charset="utf-8" class="gsc-search-box"
+ onsubmit="return submit_search()">
+ <table class="gsc-search-box" cellpadding="0" cellspacing="0"><tbody>
+ <tr>
+ <td class="gsc-input">
+ <input id="search_autocomplete" class="gsc-input" type="text" size="33" autocomplete="off"
+ title="search developer docs" name="q"
+ value="search developer docs"
+ onFocus="search_focus_changed(this, true)"
+ onBlur="search_focus_changed(this, false)"
+ onkeydown="return search_changed(event, true, '<?cs var:toroot?>')"
+ onkeyup="return search_changed(event, false, '<?cs var:toroot?>')" />
+ <div id="search_filtered_div" class="no-display">
+ <table id="search_filtered" cellspacing=0>
+ </table>
+ </div>
+ </td>
+ <td class="gsc-search-button">
+ <input type="submit" value="Search" title="search" id="search-button" class="gsc-search-button" />
+ </td>
+ <td class="gsc-clear-button">
+ <div title="clear results" class="gsc-clear-button">&nbsp;</div>
+ </td>
+ </tr></tbody>
+ </table>
+ </form>
+ </div><!-- searchForm -->
+ </div><!-- search --><?cs
+/def ?>
+
+<?cs include:"customization.cs" ?>
diff --git a/tools/droiddoc/templates/navtree_data.cs b/tools/droiddoc/templates/navtree_data.cs
new file mode 100644
index 000000000..c7072328e
--- /dev/null
+++ b/tools/droiddoc/templates/navtree_data.cs
@@ -0,0 +1,4 @@
+var NAVTREE_DATA =
+<?cs var:reference_tree ?>
+;
+
diff --git a/tools/droiddoc/templates/nosidenavpage.cs b/tools/droiddoc/templates/nosidenavpage.cs
new file mode 100644
index 000000000..1dec41e79
--- /dev/null
+++ b/tools/droiddoc/templates/nosidenavpage.cs
@@ -0,0 +1,23 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<a name="top"></a>
+<?cs call:custom_masthead() ?>
+
+<div id="body-content">
+<div id="doc-content" style="position:relative;">
+
+<?cs call:tag_list(root.descr) ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
+
+
+
diff --git a/tools/droiddoc/templates/package-descr.cs b/tools/droiddoc/templates/package-descr.cs
new file mode 100644
index 000000000..385ce23c6
--- /dev/null
+++ b/tools/droiddoc/templates/package-descr.cs
@@ -0,0 +1,32 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+ <strong>
+ <div class="jd-page_title-prefix">package</div>
+ </strong>
+ <h1><?cs var:package.name ?></b></h1>
+ <div class="jd-nav">
+ <a class="jd-navlink" href="package-summary.html">Classes</a> |
+ Description
+ </div>
+</div><!-- end header -->
+
+<div id="jd-content">
+<div class="jd-descr">
+<p><?cs call:tag_list(package.descr) ?></p>
+</div>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/package-list.cs b/tools/droiddoc/templates/package-list.cs
new file mode 100644
index 000000000..7f0f88980
--- /dev/null
+++ b/tools/droiddoc/templates/package-list.cs
@@ -0,0 +1,2 @@
+<?cs each:pkg=docs.packages ?><?cs var: pkg.name ?>
+<?cs /each ?>
diff --git a/tools/droiddoc/templates/package.cs b/tools/droiddoc/templates/package.cs
new file mode 100644
index 000000000..7d1936d58
--- /dev/null
+++ b/tools/droiddoc/templates/package.cs
@@ -0,0 +1,52 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+ package
+ <h1><?cs var:package.name ?></h1>
+
+ <div class="jd-nav">
+ <?cs if:subcount(package.shortDescr) ?>
+ Classes |
+ <a class="jd-navlink" href="package-descr.html">Description</a>
+ <?cs /if ?>
+ </div>
+</div>
+
+<div id="jd-content">
+
+<?cs if:subcount(package.shortDescr) ?>
+ <div class="jd-descr">
+ <p><?cs call:tag_list(package.shortDescr) ?>
+ <span class="jd-more"><a href="package-descr.html">more...</a></span></p>
+ </div>
+<?cs /if ?>
+
+<?cs def:class_table(label, classes) ?>
+ <?cs if:subcount(classes) ?>
+ <h3><?cs var:label ?></h3>
+ <div class="jd-sumtable">
+ <?cs call:class_link_table(classes) ?>
+ </div>
+ <?cs /if ?>
+<?cs /def ?>
+
+<?cs call:class_table("Interfaces", package.interfaces) ?>
+<?cs call:class_table("Classes", package.classes) ?>
+<?cs call:class_table("Enums", package.enums) ?>
+<?cs call:class_table("Exceptions", package.exceptions) ?>
+<?cs call:class_table("Errors", package.errors) ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/packages.cs b/tools/droiddoc/templates/packages.cs
new file mode 100644
index 000000000..a358dca9f
--- /dev/null
+++ b/tools/droiddoc/templates/packages.cs
@@ -0,0 +1,38 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<body class="gc-documentation">
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content">
+
+<div id="jd-header">
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<div class="jd-descr">
+<p><?cs call:tag_list(root.descr) ?></p>
+</div>
+
+<?cs set:count = #1 ?>
+<table class="jd-sumtable">
+<?cs each:pkg = docs.packages ?>
+ <tr <?cs if:count % #2 ?>class="alt-color"<?cs /if ?> >
+ <td class="jd-linkcol"><?cs call:package_link(pkg) ?></td>
+ <td class="jd-descrcol" width="100%"><?cs call:tag_list(pkg.shortDescr) ?>&nbsp;</td>
+ </tr>
+<?cs set:count = count + #1 ?>
+<?cs /each ?>
+</table>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/sample.cs b/tools/droiddoc/templates/sample.cs
new file mode 100644
index 000000000..7ab5dc9c2
--- /dev/null
+++ b/tools/droiddoc/templates/sample.cs
@@ -0,0 +1,34 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<?cs set:guide="true" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+
+ <span class="crumb">
+ <a href="<?cs var:toroot ?>guide/samples/index.html">Sample Code &gt;</a>
+
+ </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<p><a href="<?cs var:realFile ?>">Original <?cs var:realFile ?></a></p>
+
+<!-- begin file contents -->
+<pre class="Code prettyprint"><?cs var:fileContents ?></pre>
+<!-- end file contents -->
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div> <!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/sampleindex.cs b/tools/droiddoc/templates/sampleindex.cs
new file mode 100644
index 000000000..6e57cfdf4
--- /dev/null
+++ b/tools/droiddoc/templates/sampleindex.cs
@@ -0,0 +1,48 @@
+<?cs include:"doctype.cs" ?>
+<?cs include:"macros.cs" ?>
+<?cs set:guide="true" ?>
+<html>
+<?cs include:"head_tag.cs" ?>
+<?cs include:"header.cs" ?>
+
+<div class="g-unit" id="doc-content"><a name="top"></a>
+
+<div id="jd-header" class="guide-header">
+
+ <span class="crumb">
+ <a href="<?cs var:toroot ?>guide/samples/index.html">Sample Code &gt;</a>
+
+ </span>
+<h1><?cs var:page.title ?></h1>
+</div>
+
+<div id="jd-content">
+
+<?cs var:summary ?>
+
+<?cs if:subcount(subdirs) ?>
+ <h2>Subdirectories</h2>
+ <ul class="nolist">
+ <?cs each:dir=subdirs ?>
+ <li><a href="<?cs var:dir.name ?>/index.html"><?cs var:dir.name ?>/</a></li>
+ <?cs /each ?>
+ </ul>
+<?cs /if ?>
+
+<?cs if:subcount(files) ?>
+ <h2>Files</h2>
+ <ul class="nolist">
+ <?cs each:file=files ?>
+ <li><a href="<?cs var:file.href ?>"><?cs var:file.name ?></a></li>
+ <?cs /each ?>
+ </ul>
+<?cs /if ?>
+
+<?cs include:"footer.cs" ?>
+</div><!-- end jd-content -->
+</div><!-- end doc-content -->
+
+<?cs include:"trailer.cs" ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/todo.cs b/tools/droiddoc/templates/todo.cs
new file mode 100644
index 000000000..e9f723755
--- /dev/null
+++ b/tools/droiddoc/templates/todo.cs
@@ -0,0 +1,99 @@
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title><?cs var:page.title ?></title>
+ <style type="text/css">
+ table {
+ border-width: 1px 1px 1px 1px;
+ border-spacing: 0px;
+ border-style: solid solid solid solid;
+ border-color: black black black black;
+ border-collapse: collapse;
+ background-color: white;
+ }
+ table th {
+ border-width: 1px 1px 1px 1px;
+ padding: 1px 4px 1px 3px;
+ border-style: inset inset inset inset;
+ border-color: gray gray gray gray;
+ background-color: white;
+ }
+ table td {
+ border-width: 1px 1px 1px 1px;
+ padding: 1px 4px 1px 3px;
+ border-style: inset inset inset inset;
+ border-color: gray gray gray gray;
+ background-color: white;
+ }
+ </style>
+</head>
+<body>
+<h1><?cs var:page.title ?></h1>
+
+<h2>Overall</h2>
+<table>
+<tr><th>Errors</th><td><?cs var:all.errorCount ?></td></tr>
+<tr><th>Percent Good</th><td><?cs var:all.percentGood ?></td></tr>
+<tr><th>Total Comments</th><td><?cs var:all.totalCount ?></td></tr>
+</table>
+
+<h2>Package Summary</h2>
+
+<table>
+<tr>
+ <th>Package</th>
+ <th>Errors</th>
+ <th>Percent Good</th>
+ <th>Total</th>
+</tr>
+<?cs each:pkg=packages ?>
+<tr>
+ <td><?cs var:pkg.name ?></td>
+ <td><?cs var:pkg.errorCount ?></td>
+ <td><?cs var:pkg.percentGood ?></td>
+ <td><?cs var:pkg.totalCount ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+
+<h2>Class Summary</h3>
+
+<table>
+<tr>
+ <th>Class</th>
+ <th>Errors</th>
+ <th>Percent Good</th>
+ <th>Total</th>
+</tr>
+<?cs each:cl=classes ?>
+<tr>
+ <td><a href="#class_<?cs var:cl.qualified ?>"><?cs var:cl.qualified ?></a></td>
+ <td><?cs var:cl.errorCount ?></td>
+ <td><?cs var:cl.percentGood ?></td>
+ <td><?cs var:cl.totalCount ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+<h2>Detail</h2>
+
+<?cs each:cl=classes ?>
+<h3><a name="class_<?cs var:cl.qualified ?>"><?cs var:cl.qualified ?></a></h3>
+<p>Errors: <?cs var:cl.errorCount ?><br/>
+Total: <?cs var:cl.totalCount ?><br/>
+Percent Good: <?cs var:cl.percentGood ?></p>
+<table>
+<?cs each:err=cl.errors ?>
+<tr>
+ <td><?cs var:err.pos ?></td>
+ <td><?cs var:err.name ?></td>
+ <td><?cs var:err.descr ?></td>
+</tr>
+<?cs /each ?>
+</table>
+
+<?cs /each ?>
+
+</body>
+</html>
diff --git a/tools/droiddoc/templates/trailer.cs b/tools/droiddoc/templates/trailer.cs
new file mode 100644
index 000000000..155ba584b
--- /dev/null
+++ b/tools/droiddoc/templates/trailer.cs
@@ -0,0 +1,11 @@
+</div> <!-- end body-content --> <?cs # normally opened by header.cs ?>
+
+<script type="text/javascript">
+init(); /* initialize android-developer-docs.js */
+var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
+document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
+</script>
+<script type="text/javascript">
+var pageTracker = _gat._getTracker("UA-5831155-1");
+pageTracker._trackPageview();
+</script> \ No newline at end of file
diff --git a/tools/droiddoc/test/generics/Android.mk b/tools/droiddoc/test/generics/Android.mk
new file mode 100644
index 000000000..0c808fda4
--- /dev/null
+++ b/tools/droiddoc/test/generics/Android.mk
@@ -0,0 +1,28 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(call all-subdir-java-files)
+
+LOCAL_MODULE:=test_generics
+LOCAL_DROIDDOC_OPTIONS:=\
+ -stubs __test_generics__
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=tools/droiddoc/templates-google
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-google
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+include $(BUILD_DROIDDOC)
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java b/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java
new file mode 100644
index 000000000..6bef81216
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/AbsListView.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class AbsListView implements AdapterView<ListAdapter> {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java b/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java
new file mode 100644
index 000000000..c041dd1a1
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Adapter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class Adapter {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java b/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java
new file mode 100644
index 000000000..de4f8f17f
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/AdapterView.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public interface AdapterView<T extends Adapter> {
+}
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Bar.java b/tools/droiddoc/test/generics/src/com/android/generics/Bar.java
new file mode 100644
index 000000000..bd9fcbcfe
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Bar.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public interface Bar<K> {
+ public K bar(K arg);
+}
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Foo.java b/tools/droiddoc/test/generics/src/com/android/generics/Foo.java
new file mode 100644
index 000000000..d5d07eb5f
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Foo.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class Foo<V> {
+ public Foo(V v) {
+ }
+
+ public V foo(V arg) {
+ return null;
+ }
+}
+
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java b/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java
new file mode 100644
index 000000000..7ea356762
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/FooBar.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class FooBar<K,V,L> extends Foo<V> implements Bar<K> {
+ public class C
+ {
+ }
+
+ public class CI extends C implements Iface
+ {
+ }
+
+ public FooBar(K k) {
+ super(null);
+ throw new RuntimeException("!");
+ }
+
+ public K bar(K arg) {
+ return null;
+ }
+
+ public FooBar<K,? extends Foo,L> a(K arg) {
+ return null;
+ }
+
+ public FooBar<V,K,L> b(Bar<? extends K> arg) {
+ return null;
+ }
+
+ public <L extends C & Iface> void f(L arg) {
+ }
+
+ public V v;
+}
+
+
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/Iface.java b/tools/droiddoc/test/generics/src/com/android/generics/Iface.java
new file mode 100644
index 000000000..03aa2ddd1
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/Iface.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public interface Iface {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java b/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java
new file mode 100644
index 000000000..5f88a561e
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/ListAdapter.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class ListAdapter extends Adapter {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java b/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java
new file mode 100644
index 000000000..9d394ae17
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/TestComparable.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public interface TestComparable<T> {
+}
diff --git a/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java b/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java
new file mode 100644
index 000000000..efb1d1889
--- /dev/null
+++ b/tools/droiddoc/test/generics/src/com/android/generics/TestEnum.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.generics;
+
+public class TestEnum<E extends TestEnum<E>> implements TestComparable<E> {
+}
+
diff --git a/tools/droiddoc/test/stubs/Android.mk b/tools/droiddoc/test/stubs/Android.mk
new file mode 100644
index 000000000..fc971e1c8
--- /dev/null
+++ b/tools/droiddoc/test/stubs/Android.mk
@@ -0,0 +1,29 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=$(call all-java-files-under,src)
+
+LOCAL_MODULE:=test_stubs
+LOCAL_DROIDDOC_OPTIONS:=\
+ -stubs $(OUT_DIR)/__test_stubs__
+
+LOCAL_DROIDDOC_CUSTOM_TEMPLATE_DIR:=tools/droiddoc/templates-google
+LOCAL_DROIDDOC_CUSTOM_ASSET_DIR:=assets-google
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+
+include $(BUILD_DROIDDOC)
+
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java
new file mode 100644
index 000000000..b6144b64b
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Annot.java
@@ -0,0 +1,8 @@
+package com.android.stubs;
+@java.lang.annotation.Documented()
+@java.lang.annotation.Retention(value=java.lang.annotation.RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target(value={java.lang.annotation.ElementType.TYPE})
+public @interface Annot
+{
+java.lang.String value() default "yo\u1234";
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java
new file mode 100644
index 000000000..b0c87e756
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/InterfaceEnum.java
@@ -0,0 +1,9 @@
+package com.android.stubs;
+public enum InterfaceEnum
+ implements com.android.stubs.Parent.Interface
+{
+VAL();
+public void method() { throw new RuntimeException("Stub!"); }
+public static final java.lang.Object OBJECT;
+static { OBJECT = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java
new file mode 100644
index 000000000..132d56679
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Parent.java
@@ -0,0 +1,27 @@
+package com.android.stubs;
+@com.android.stubs.Annot(value="asdf")
+public class Parent
+{
+public static interface Interface
+{
+public void method();
+}
+public Parent() { throw new RuntimeException("Stub!"); }
+public java.lang.String methodString() { throw new RuntimeException("Stub!"); }
+public int method(boolean b, char c, int i, long l, float f, double d) { throw new RuntimeException("Stub!"); }
+protected void protectedMethod() { throw new RuntimeException("Stub!"); }
+public static final byte public_static_final_byte = 42;
+public static final short public_static_final_short = 43;
+public static final int public_static_final_int = 44;
+public static final long public_static_final_long = 45L;
+public static final char public_static_final_char = 4660;
+public static final float public_static_final_float = 42.1f;
+public static final double public_static_final_double = 42.2;
+public static int public_static_int;
+public static final java.lang.String public_static_final_String = "ps\u1234fS";
+public static java.lang.String public_static_String;
+public static com.android.stubs.Parent public_static_Parent;
+public static final com.android.stubs.Parent public_static_final_Parent;
+public static final com.android.stubs.Parent public_static_final_Parent_null;
+static { public_static_final_Parent = null; public_static_final_Parent_null = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java
new file mode 100644
index 000000000..ecfd9d43d
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/SomeEnum.java
@@ -0,0 +1,7 @@
+package com.android.stubs;
+public enum SomeEnum
+{
+A(),
+B(),
+C();
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java
new file mode 100644
index 000000000..3f5a791cc
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/Types.java
@@ -0,0 +1,33 @@
+package com.android.stubs;
+public class Types
+{
+public static interface Interface
+{
+public static final boolean public_static_final_boolean = false;
+public static final char public_static_final_char = 0;
+public static final short public_static_final_short = 0;
+public static final int public_static_final_int = 0;
+public static final long public_static_final_long = 0L;
+public static final float public_static_final_float = 0.0f;
+public static final double public_static_final_double = 0.0;
+public static final java.lang.Object public_static_final_Object = null;
+}
+protected Types() { throw new RuntimeException("Stub!"); }
+public final boolean public_final_boolean;
+public final char public_final_char;
+public final short public_final_short;
+public final int public_final_int;
+public final long public_final_long;
+public final float public_final_float;
+public final double public_final_double;
+public final java.lang.Object public_final_Object;
+public static final boolean public_static_final_boolean;
+public static final char public_static_final_char;
+public static final short public_static_final_short;
+public static final int public_static_final_int;
+public static final long public_static_final_long;
+public static final float public_static_final_float;
+public static final double public_static_final_double;
+public static final java.lang.Object public_static_final_Object;
+static { public_static_final_boolean = false; public_static_final_char = 0; public_static_final_short = 0; public_static_final_int = 0; public_static_final_long = 0; public_static_final_float = 0; public_static_final_double = 0; public_static_final_Object = null; }
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java
new file mode 100644
index 000000000..f3cb88834
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/A.java
@@ -0,0 +1,14 @@
+package com.android.stubs.a;
+public abstract class A
+ extends com.android.stubs.Parent
+ implements com.android.stubs.Parent.Interface, com.android.stubs.a.SomeInterface
+{
+public class Inner
+{
+public Inner() { throw new RuntimeException("Stub!"); }
+}
+protected A(int a) { throw new RuntimeException("Stub!"); }
+public com.android.stubs.a.A varargs(com.android.stubs.Parent[]... args) { throw new RuntimeException("Stub!"); }
+public void method() { throw new RuntimeException("Stub!"); }
+public abstract java.lang.String[] stringArrayMethod() throws java.io.IOException;
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java
new file mode 100644
index 000000000..c24981b55
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/a/SomeInterface.java
@@ -0,0 +1,4 @@
+package com.android.stubs.a;
+public interface SomeInterface
+{
+}
diff --git a/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java b/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java
new file mode 100644
index 000000000..5db2fcecc
--- /dev/null
+++ b/tools/droiddoc/test/stubs/expected/com/android/stubs/b/B.java
@@ -0,0 +1,5 @@
+package com.android.stubs.b;
+public class B
+{
+public B() { throw new RuntimeException("Stub!"); }
+}
diff --git a/tools/droiddoc/test/stubs/func.sh b/tools/droiddoc/test/stubs/func.sh
new file mode 100644
index 000000000..1ad4bd511
--- /dev/null
+++ b/tools/droiddoc/test/stubs/func.sh
@@ -0,0 +1,68 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+export A_STUBS=out/stubs/a/stubs
+export B_STUBS=out/stubs/b/stubs
+export EXPECTED_STUBS=out/stubs/expected/stubs
+export EXPECTED=$DIR/expected
+
+function build_stubs()
+{
+ ID=$1
+ SRC_DIR=$2
+ STUBS_DIR=$3
+
+ OBJ_DIR=out/stubs/$ID
+
+ rm -rf $OBJ_DIR &> /dev/null
+ mkdir -p $OBJ_DIR
+
+ find $SRC_DIR -name '*.java' > $OBJ_DIR/javadoc-src-list
+ ( \
+ LD_LIBRARY_PATH=out/host/darwin-x86/lib \
+ javadoc \
+ \@$OBJ_DIR/javadoc-src-list \
+ -J-Xmx512m \
+ -J-Djava.library.path=out/host/darwin-x86/lib \
+ \
+ -quiet \
+ -doclet DroidDoc \
+ -docletpath out/host/darwin-x86/framework/clearsilver.jar:out/host/darwin-x86/framework/droiddoc.jar \
+ -templatedir tools/droiddoc/templates \
+ -classpath out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
+ -sourcepath $SRC_DIR:out/target/common/obj/JAVA_LIBRARIES/core_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/ext_intermediates/classes.jar:out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
+ -d $OBJ_DIR/docs \
+ -hdf page.build MAIN-eng.joeo.20080710.121320 -hdf page.now "10 Jul 2008 12:13" \
+ -stubs $STUBS_DIR \
+ -stubpackages com.android.stubs:com.android.stubs.a:com.android.stubs.b:com.android.stubs.hidden \
+ && rm -rf $OBJ_DIR/docs/assets \
+ && mkdir -p $OBJ_DIR/docs/assets \
+ && cp -fr tools/droiddoc/templates/assets/* $OBJ_DIR/docs/assets/ \
+ )# || (rm -rf $OBJ_DIR; exit 45)
+}
+
+function compile_stubs()
+{
+ ID=$1
+ STUBS_DIR=$2
+
+ OBJ_DIR=out/stubs/$ID
+ CLASS_DIR=$OBJ_DIR/class
+ mkdir -p $CLASS_DIR
+
+ find $STUBS_DIR -name "*.java" > $OBJ_DIR/java-src-list
+ javac @$OBJ_DIR/java-src-list -d $CLASS_DIR
+}
diff --git a/tools/droiddoc/test/stubs/run.sh b/tools/droiddoc/test/stubs/run.sh
new file mode 100755
index 000000000..f237a7d92
--- /dev/null
+++ b/tools/droiddoc/test/stubs/run.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+DIR=tools/droiddoc/test/stubs
+
+pushd $TOP
+
+. $TOP/$DIR/func.sh
+
+mkdir -p out/stubs_compiled
+find $DIR/src -name "*.java" | xargs javac -d out/stubs_compiled
+
+build_stubs a $DIR/src $A_STUBS
+build_stubs b $A_STUBS $B_STUBS
+
+compile_stubs a $A_STUBS
+
+echo EXPECTED
+diff -r $DIR/expected $A_STUBS
+echo TWICE STUBBED
+diff -r $A_STUBS $B_STUBS
+
+popd &> /dev/null
+
+
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java
new file mode 100644
index 000000000..fe9226f88
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Annot.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs;
+
+import java.lang.annotation.*;
+
+/**
+ * poop
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface Annot {
+ String value() default "yo\u1234";
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java b/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java
new file mode 100644
index 000000000..1e64f579c
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/InterfaceEnum.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs;
+
+public enum InterfaceEnum implements Parent.Interface {
+ VAL;
+ public static final Object OBJECT = new Object();
+ public void method() { }
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java
new file mode 100644
index 000000000..577db3852
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Parent.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs;
+
+@Annot("asdf")
+public class Parent {
+ public static final byte public_static_final_byte = 42;
+ public static final short public_static_final_short = 43;
+ public static final int public_static_final_int = 44;
+ public static final long public_static_final_long = 45;
+ public static final char public_static_final_char = '\u1234';
+ public static final float public_static_final_float = 42.1f;
+ public static final double public_static_final_double = 42.2;
+ public static int public_static_int = 1;
+ public static final String public_static_final_String = "ps\u1234fS";
+ public static String public_static_String = "psS";
+ public static Parent public_static_Parent = new Parent();
+ public static final Parent public_static_final_Parent = new Parent();
+ public static final Parent public_static_final_Parent_null = null;
+
+ public interface Interface {
+ void method();
+ }
+
+ public Parent() {
+ }
+
+ public String methodString() {
+ return "yo";
+ }
+
+ public int method(boolean b, char c, int i, long l, float f, double d) {
+ return 1;
+ }
+
+ protected void protectedMethod() {
+ }
+
+ void packagePrivateMethod() {
+ }
+
+ /** @hide */
+ public void hiddenMethod() {
+ }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java b/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java
new file mode 100644
index 000000000..51c50003b
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/SomeEnum.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs;
+
+public enum SomeEnum {
+ A, B, C
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java b/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java
new file mode 100644
index 000000000..5e24a101e
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/Types.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs;
+
+public class Types {
+ public final boolean public_final_boolean;
+ public final char public_final_char;
+ public final short public_final_short;
+ public final int public_final_int;
+ public final long public_final_long;
+ public final float public_final_float;
+ public final double public_final_double;
+ public final Object public_final_Object;
+
+ public static final boolean public_static_final_boolean;
+ public static final char public_static_final_char;
+ public static final short public_static_final_short;
+ public static final int public_static_final_int;
+ public static final long public_static_final_long;
+ public static final float public_static_final_float;
+ public static final double public_static_final_double;
+ public static final Object public_static_final_Object;
+
+ /** @hide */
+ public Types() {
+ public_final_boolean = false;
+ public_final_char = 0;
+ public_final_short = 0;
+ public_final_int = 0;
+ public_final_long = 0;
+ public_final_float = 0;
+ public_final_double = 0;
+ public_final_Object = null;
+ }
+
+ static {
+ public_static_final_boolean = false;
+ public_static_final_char = 0;
+ public_static_final_short = 0;
+ public_static_final_int = 0;
+ public_static_final_long = 0;
+ public_static_final_float = 0;
+ public_static_final_double = 0;
+ public_static_final_Object = null;
+ }
+
+ public interface Interface {
+ public static final boolean public_static_final_boolean = false;
+ public static final char public_static_final_char = 0;
+ public static final short public_static_final_short = 0;
+ public static final int public_static_final_int = 0;
+ public static final long public_static_final_long = 0;
+ public static final float public_static_final_float = 0;
+ public static final double public_static_final_double = 0;
+ public static final Object public_static_final_Object = null;
+ }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java b/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java
new file mode 100644
index 000000000..cebeaf1c6
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/a/A.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.a;
+
+import com.android.stubs.Parent;
+
+public abstract class A extends Parent implements Parent.Interface, SomeInterface {
+ protected A(int a) {
+ super();
+ }
+
+ public A varargs(Parent... args) {
+ return null;
+ }
+
+ public void method() {
+ }
+ public abstract String[] stringArrayMethod() throws java.io.IOException;
+
+ public class Inner {
+ int method() {
+ return 1;
+ }
+ int field;
+ }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java b/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java
new file mode 100644
index 000000000..6f5c3e070
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/a/SomeInterface.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.a;
+
+public interface SomeInterface {
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java b/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java
new file mode 100644
index 000000000..7febe332e
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/b/B.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.b;
+
+import com.android.stubs.Parent;
+import com.android.stubs.a.A;
+
+public class B {
+ Parent method(Parent p) {
+ return null;
+ }
+}
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java
new file mode 100644
index 000000000..39ece6e66
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/Hidden.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.c;
+
+/** @hide */
+public class Hidden {
+
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java
new file mode 100644
index 000000000..0380f437f
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/HiddenOuter.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.c;
+
+/** @hide */
+public class HiddenOuter {
+
+ public class NotHiddenInner {
+ }
+}
+
diff --git a/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java
new file mode 100644
index 000000000..165735c62
--- /dev/null
+++ b/tools/droiddoc/test/stubs/src/com/android/stubs/hidden/PackagePrivate.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.stubs.c;
+
+class PackagePrivate {
+
+}
+
diff --git a/tools/dump-package-stats b/tools/dump-package-stats
new file mode 100755
index 000000000..589bab58f
--- /dev/null
+++ b/tools/dump-package-stats
@@ -0,0 +1,152 @@
+#!/bin/bash
+#
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+PROGNAME=`basename $0`
+
+function fail ()
+{
+ if [ ! -z "$@" ]
+ then
+ echo "$PROGNAME: ERROR: $@" >&2
+ fi
+ echo "$PROGNAME: ERROR: failed." >&2
+ exit 1
+}
+
+function usage ()
+{
+ cat << HERE
+usage: $PROGNAME <.jar/.apk-file-list>
+ Dumps a summary of the compressed and uncompressed sizes of various
+ types of files in each package. Emits one line per package.
+ Packages must be zipfiles, readable using "unzip".
+
+ Example output line:
+
+ filesize=642684 all=603288/919304 dex=119529/353815 name="out/App.apk"
+
+ filesize: the size of the package on disk
+ name: the name of the package as passed to $PROGNAME
+
+ These fields are presented as <uncompressed bytes>/<compressed bytes>:
+
+ all: the sum of all entries in the package
+ dex: the sum of all "*.dex" entries in the package
+HERE
+ exit 1
+}
+
+if [ $# -lt 1 ]
+then
+ usage
+fi
+
+UNAME=`uname`
+if [ "x$UNAME" = "xDarwin" ]
+then
+ statArgs="-f %z"
+elif [ "x$UNAME" = "xLinux" ]
+then
+ statArgs="-c %s"
+else
+ fail "Unknown uname $UNAME"
+fi
+
+function printFileSize ()
+{
+ stat $statArgs $1
+}
+
+for file
+do
+ if [ ! -f "$file" ]
+ then
+ fail "$file doesn't exist or isn't a file"
+ fi
+ unzip -lv "$file" | awk '
+ BEGIN {
+ total_compressed = 0;
+ total_uncompressed = 0;
+ dex_compressed = 0;
+ dex_uncompressed = 0;
+ }
+
+ # Make sure the output of unzip -lv looks like something we expect.
+ #
+ NR == "1" {
+ if ($1 != "Archive:") {
+ print "'$PROGNAME': ERROR: Unexpected zip listing format" > \
+ "/dev/stderr";
+ print "'$PROGNAME': ERROR: Line 1 is \"" $0 "\"" > \
+ "/dev/stderr";
+ failed = 1;
+ exit 1;
+ }
+ }
+ NR == "2" {
+ if (NF != "8" ||
+ $1 != "Length" ||
+ $2 != "Method" ||
+ $3 != "Size" ||
+ $4 != "Ratio" ||
+ $5 != "Date" ||
+ $6 != "Time" ||
+ $7 != "CRC-32" ||
+ $8 != "Name")
+ {
+ print "'$PROGNAME': ERROR: Unexpected zip listing format" > \
+ "/dev/stderr";
+ print "'$PROGNAME': ERROR: Line 2 is \"" $0 "\"" > \
+ "/dev/stderr";
+ failed = 1;
+ exit 1;
+ } else {
+ saw_listing = 1;
+ }
+ }
+
+ # Only look for lines where the ratio is the fourth column;
+ # this filters out the header and footer.
+ #
+ $4 ~ /%$/ {
+ uncompressed = $1;
+ compressed = $3;
+ if ($0 ~ /.dex$/) {
+ dex_compressed += compressed;
+ dex_uncompressed += uncompressed;
+ }
+ total_compressed += compressed;
+ total_uncompressed += uncompressed;
+ }
+ { next }
+
+ END {
+ if (!failed && saw_listing) {
+ print "filesize='$(printFileSize "$file")'",
+ "all=" total_compressed "/" total_uncompressed,
+ "dex=" dex_compressed "/" dex_uncompressed,
+ "name=\"'"$file"'\"";
+ } else {
+ exit 1;
+ }
+ }
+ '
+ if [ $? -ne 0 ]
+ then
+ fail "Could not get stats for $file"
+ fi
+done
diff --git a/tools/fileslist.py b/tools/fileslist.py
new file mode 100755
index 000000000..ae1b4b6ff
--- /dev/null
+++ b/tools/fileslist.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2009 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the 'License');
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an 'AS IS' BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+import os, sys
+
+def get_file_size(path):
+ st = os.lstat(path)
+ return st.st_size;
+
+def main(argv):
+ output = []
+ roots = argv[1:]
+ for root in roots:
+ base = len(root[:root.rfind(os.path.sep)])
+ for dir, dirs, files in os.walk(root):
+ relative = dir[base:]
+ for f in files:
+ try:
+ row = (
+ get_file_size(os.path.sep.join((dir, f))),
+ os.path.sep.join((relative, f)),
+ )
+ output.append(row)
+ except os.error:
+ pass
+ for row in output:
+ print "%12d %s" % row
+
+if __name__ == '__main__':
+ main(sys.argv)
+
diff --git a/tools/findleaves.sh b/tools/findleaves.sh
new file mode 100755
index 000000000..7cc0fa775
--- /dev/null
+++ b/tools/findleaves.sh
@@ -0,0 +1,109 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Finds files with the specified name under a particular directory, stopping
+# the search in a given subdirectory when the file is found.
+#
+
+set -o nounset # fail when dereferencing unset variables
+set -o errexit # fail if any subcommand fails
+
+progName=`basename $0`
+
+function warn() {
+ echo "$progName: $@" >&2
+}
+
+function trace() {
+ echo "$progName: $@"
+}
+
+function usage() {
+ if [[ $# > 0 ]]
+ then
+ warn $@
+ fi
+ cat <<-EOF
+Usage: $progName [<options>] <dirlist> <filename>
+Options:
+ --mindepth=<mindepth>
+ --maxdepth=<maxdepth>
+ Both behave in the same way as their find(1) equivalents.
+ --prune=<glob>
+ Avoids returning results from any path matching the given glob-style
+ pattern (e.g., "*/out/*"). May be used multiple times.
+EOF
+ exit 1
+}
+
+function fail() {
+ warn $@
+ exit 1
+}
+
+if [ $# -lt 2 ]
+then
+ usage
+fi
+
+findargs=""
+while [[ "${1:0:2}" == "--" ]]
+do
+ arg=${1:2}
+ name=${arg%%=*}
+ value=${arg##*=}
+ if [[ "$name" == "mindepth" || "$name" == "maxdepth" ]]
+ then
+ # Add to beginning of findargs; these must come before the expression.
+ findargs="-$name $value $findargs"
+ elif [[ "$name" == "prune" ]]
+ then
+ # Add to end of findargs; these are part of the expression.
+ findargs="$findargs -path $value -prune -or"
+ fi
+ shift
+done
+
+nargs=$#
+# The filename is the last argument
+filename="${!nargs}"
+
+# Print out all files that match, as long as the path isn't explicitly
+# pruned. This will print out extraneous results from directories whose
+# parents have a match. These are filtered out by the awk script below.
+find "${@:0:$nargs}" $findargs -type f -name "$filename" -print |
+
+# Only pass along the directory of each match.
+sed -e 's/\/[^\/]*$/\//' |
+
+# Sort the output, so directories appear immediately before their contents.
+# If there are any duplicates, the awk script will implicitly ignore them.
+# The LC_ALL=C forces sort(1) to use bytewise ordering instead of listening
+# to the locale, which may do case-insensitive and/or alphanumeric-only
+# sorting.
+LC_ALL=C sort |
+
+# Always print the first line, which can't possibly be covered by a
+# parent directory match. After that, only print lines where the last
+# line printed isn't a prefix.
+awk -v "filename=$filename" '
+ (NR == 1) || (index($0, last) != 1) {
+ last = $0;
+ printf("%s%s\n", $0, filename);
+ }
+'
diff --git a/tools/fixlinebreaks.sh b/tools/fixlinebreaks.sh
new file mode 100755
index 000000000..85b7b608e
--- /dev/null
+++ b/tools/fixlinebreaks.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+#
+# Convert EOL convention on source files from CRLF to LF.
+#
+
+echo "Scanning..."
+FILES=`find . \( -iname '*.c' -o -iname '*.cpp' -o -iname '*.h' -o -iname '*.mk' -o -iname '*.html' -o -iname '*.css' \) -print`
+
+echo "Converting..."
+for file in $FILES ; do
+ echo $file
+ tr -d \\r < $file > _temp_file
+ mv _temp_file $file
+done
+exit 0
+
diff --git a/tools/fs_config/Android.mk b/tools/fs_config/Android.mk
new file mode 100644
index 000000000..3f2ed9530
--- /dev/null
+++ b/tools/fs_config/Android.mk
@@ -0,0 +1,27 @@
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+ifneq ($(TARGET_SIMULATOR),true)
+
+LOCAL_SRC_FILES := fs_config.c
+LOCAL_MODULE := fs_config
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_MODULE_TAGS := eng
+
+include $(BUILD_HOST_EXECUTABLE)
+
+endif # !TARGET_SIMULATOR
diff --git a/tools/fs_config/fs_config.c b/tools/fs_config/fs_config.c
new file mode 100644
index 000000000..5b99b3021
--- /dev/null
+++ b/tools/fs_config/fs_config.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "private/android_filesystem_config.h"
+
+// This program takes a list of files and directories (indicated by a
+// trailing slash) on the stdin, and prints to stdout each input
+// filename along with its desired uid, gid, and mode (in octal).
+// The leading slash should be stripped from the input.
+//
+// Example input:
+//
+// system/etc/dbus.conf
+// data/app/
+//
+// Output:
+//
+// system/etc/dbus.conf 1002 1002 440
+// data/app 1000 1000 771
+//
+// Note that the output will omit the trailing slash from
+// directories.
+
+int main(int argc, char** argv) {
+ char buffer[1024];
+
+ while (fgets(buffer, 1023, stdin) != NULL) {
+ int is_dir = 0;
+ int i;
+ for (i = 0; i < 1024 && buffer[i]; ++i) {
+ switch (buffer[i]) {
+ case '\n':
+ buffer[i-is_dir] = '\0';
+ i = 1025;
+ break;
+ case '/':
+ is_dir = 1;
+ break;
+ default:
+ is_dir = 0;
+ break;
+ }
+ }
+
+ unsigned uid = 0, gid = 0, mode = 0;
+ fs_config(buffer, is_dir, &uid, &gid, &mode);
+ printf("%s %d %d %o\n", buffer, uid, gid, mode);
+ }
+ return 0;
+}
diff --git a/tools/fs_get_stats/Android.mk b/tools/fs_get_stats/Android.mk
new file mode 100644
index 000000000..c9b4a0510
--- /dev/null
+++ b/tools/fs_get_stats/Android.mk
@@ -0,0 +1,9 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := fs_get_stats.c
+
+LOCAL_MODULE := fs_get_stats
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/fs_get_stats/fs_get_stats.c b/tools/fs_get_stats/fs_get_stats.c
new file mode 100644
index 000000000..356f6f90a
--- /dev/null
+++ b/tools/fs_get_stats/fs_get_stats.c
@@ -0,0 +1,64 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <private/android_filesystem_config.h>
+
+#define DO_DEBUG 1
+
+#define ERROR(fmt,args...) \
+ do { \
+ fprintf(stderr, "%s:%d: ERROR: " fmt, \
+ __FILE__, __LINE__, ##args); \
+ } while (0)
+
+#if DO_DEBUG
+#define DEBUG(fmt,args...) \
+ do { fprintf(stderr, "DEBUG: " fmt, ##args); } while(0)
+#else
+#define DEBUG(x...) do {} while(0)
+#endif
+
+void
+print_help(void)
+{
+ fprintf(stderr, "fs_get_stats: retrieve the target file stats "
+ "for the specified file\n");
+ fprintf(stderr, "usage: fs_get_stats cur_perms is_dir filename\n");
+ fprintf(stderr, "\tcur_perms - The current permissions of "
+ "the file\n");
+ fprintf(stderr, "\tis_dir - Is filename is a dir, 1. Otherwise, 0.\n");
+ fprintf(stderr, "\tfilename - The filename to lookup\n");
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, const char *argv[])
+{
+ char *endptr;
+ char is_dir = 0;
+ unsigned perms = 0;
+ unsigned uid = (unsigned)-1;
+ unsigned gid = (unsigned)-1;
+
+ if (argc < 4) {
+ ERROR("Invalid arguments\n");
+ print_help();
+ exit(-1);
+ }
+
+ perms = (unsigned)strtoul(argv[1], &endptr, 0);
+ if (!endptr || (endptr == argv[1]) || (*endptr != '\0')) {
+ ERROR("current permissions must be a number. Got '%s'.\n", argv[1]);
+ exit(-1);
+ }
+
+ if (!strcmp(argv[2], "1"))
+ is_dir = 1;
+
+ fs_config(argv[3], is_dir, &uid, &gid, &perms);
+ fprintf(stdout, "%d %d 0%o\n", uid, gid, perms);
+
+ return 0;
+}
diff --git a/tools/iself/Android.mk b/tools/iself/Android.mk
new file mode 100755
index 000000000..49fabff2e
--- /dev/null
+++ b/tools/iself/Android.mk
@@ -0,0 +1,23 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for iself
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DDEBUG
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/
+
+LOCAL_SRC_FILES := \
+ iself.c
+
+LOCAL_MODULE := iself
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/iself/debug.h b/tools/iself/debug.h
new file mode 100644
index 000000000..9bbf47fa3
--- /dev/null
+++ b/tools/iself/debug.h
@@ -0,0 +1,90 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR PRINT
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/iself/iself.c b/tools/iself/iself.c
new file mode 100644
index 000000000..e634a223f
--- /dev/null
+++ b/tools/iself/iself.c
@@ -0,0 +1,36 @@
+#include <debug.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+int
+main(int argc, char **argv)
+{
+ char *fname;
+ int fd;
+ char magic[4];
+
+ argc--, argv++;
+ FAILIF(argc != 1, "Expecting a file name!\n");
+ fname = *argv;
+
+ fd = open(fname, O_RDONLY);
+ FAILIF(fd < 0, "Error opening %s for reading: %s (%d)!\n",
+ fname, strerror(errno), errno);
+
+ FAILIF(4 != read(fd, magic, 4),
+ "Could not read first 4 bytes from %s: %s (%d)!\n",
+ fname, strerror(errno), errno);
+
+ if (magic[0] != 0x7f) return 1;
+ if (magic[1] != 'E') return 1;
+ if (magic[2] != 'L') return 1;
+ if (magic[3] != 'F') return 1;
+
+ return 0;
+}
diff --git a/tools/isprelinked/Android.mk b/tools/isprelinked/Android.mk
new file mode 100755
index 000000000..e085f29ea
--- /dev/null
+++ b/tools/isprelinked/Android.mk
@@ -0,0 +1,40 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for apriori
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DSUPPORT_ANDROID_PRELINK_TAGS
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DDEBUG
+
+ifeq ($(HOST_OS),windows)
+LOCAL_LDLIBS += -lintl
+endif
+
+LOCAL_SRC_FILES := \
+ isprelinked.c \
+ debug.c \
+ prelink_info.c
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ external/elfutils/lib/ \
+ external/elfutils/libelf/ \
+ external/elfutils/libebl/ \
+ external/elfcopy/
+
+LOCAL_STATIC_LIBRARIES := libelfcopy libelf libebl libebl_arm #dl
+
+LOCAL_MODULE := isprelinked
+
+include $(BUILD_HOST_EXECUTABLE)
+endif #TARGET_ARCH==arm
diff --git a/tools/isprelinked/common.h b/tools/isprelinked/common.h
new file mode 100644
index 000000000..f5d9d2ef8
--- /dev/null
+++ b/tools/isprelinked/common.h
@@ -0,0 +1,28 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+static inline int is_host_little(void)
+{
+ short val = 0x10;
+ return ((char *)&val)[0] != 0;
+}
+
+static inline long switch_endianness(long val)
+{
+ long newval;
+ ((char *)&newval)[3] = ((char *)&val)[0];
+ ((char *)&newval)[2] = ((char *)&val)[1];
+ ((char *)&newval)[1] = ((char *)&val)[2];
+ ((char *)&newval)[0] = ((char *)&val)[3];
+ return newval;
+}
+
+#endif/*COMMON_H*/
diff --git a/tools/isprelinked/debug.c b/tools/isprelinked/debug.c
new file mode 100644
index 000000000..60665430a
--- /dev/null
+++ b/tools/isprelinked/debug.c
@@ -0,0 +1,37 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ } else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n", i);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
+
diff --git a/tools/isprelinked/debug.h b/tools/isprelinked/debug.h
new file mode 100644
index 000000000..39968984b
--- /dev/null
+++ b/tools/isprelinked/debug.h
@@ -0,0 +1,88 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR PRINT
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/isprelinked/isprelinked.c b/tools/isprelinked/isprelinked.c
new file mode 100644
index 000000000..c677e39d2
--- /dev/null
+++ b/tools/isprelinked/isprelinked.c
@@ -0,0 +1,89 @@
+/* TODO:
+ 1. check the ARM EABI version--this works for versions 1 and 2.
+ 2. use a more-intelligent approach to finding the symbol table, symbol-string
+ table, and the .dynamic section.
+ 3. fix the determination of the host and ELF-file endianness
+ 4. write the help screen
+*/
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <libebl.h>
+#ifdef ARM_SPECIFIC_HACKS
+ #include <libebl_arm.h>
+#endif/*ARM_SPECIFIC_HACKS*/
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <rangesort.h>
+#include <prelink_info.h>
+#include <libgen.h>
+
+
+/* Flag set by --verbose. This variable is global as it is accessed by the
+ macro INFO() in multiple compilation unites. */
+int verbose_flag = 0;
+/* Flag set by --quiet. This variable is global as it is accessed by the
+ macro PRINT() in multiple compilation unites. */
+int quiet_flag = 0;
+
+int main(int argc, char **argv) {
+
+ argc--, argv++;
+ if (!argc)
+ return 0;
+
+ /* Check to see whether the ELF library is current. */
+ FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
+
+ const char *filename;
+ for (; argc; argc--) {
+ filename = *argv++;
+
+ Elf *elf;
+ GElf_Ehdr elf_hdr;
+ int fd;
+ int prelinked;
+ long prelink_addr = 0;
+
+ INFO("Processing file [%s]\n", filename);
+
+ fd = open(filename, O_RDONLY);
+ FAILIF(fd < 0, "open(%d): %s (%d).\n",
+ filename,
+ strerror(errno),
+ errno);
+
+ elf = elf_begin(fd, ELF_C_READ_MMAP_PRIVATE, NULL);
+ FAILIF_LIBELF(elf == NULL, elf_begin);
+
+ FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr),
+ gelf_getehdr);
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ prelinked = check_prelinked(filename, elf_hdr.e_ident[EI_DATA] == ELFDATA2LSB,
+ &prelink_addr);
+#else
+ #error 'SUPPORT_ANDROID_PRELINK_TAGS is not defined!'
+#endif
+
+ if (prelinked)
+ PRINT("%s: 0x%08x\n", filename, prelink_addr);
+ else
+ PRINT("%s: not prelinked\n", filename);
+
+ FAILIF_LIBELF(elf_end(elf), elf_end);
+ close(fd);
+ }
+
+ return 0;
+}
+
diff --git a/tools/isprelinked/prelink_info.c b/tools/isprelinked/prelink_info.c
new file mode 100644
index 000000000..21b15191f
--- /dev/null
+++ b/tools/isprelinked/prelink_info.c
@@ -0,0 +1,71 @@
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <prelink_info.h>
+#include <debug.h>
+#include <common.h>
+
+typedef struct {
+ long mmap_addr;
+ char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t __attribute__((packed));
+
+static inline void set_prelink(long *prelink_addr,
+ int elf_little,
+ prelink_info_t *info)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ if (prelink_addr) {
+ if (!(elf_little ^ is_host_little())) {
+ /* Same endianness */
+ *prelink_addr = info->mmap_addr;
+ }
+ else {
+ /* Different endianness */
+ *prelink_addr = switch_endianness(info->mmap_addr);
+ }
+ }
+}
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ int fd = open(fname, O_RDONLY);
+ FAILIF(fd < 0, "open(%s, O_RDONLY): %s (%d)!\n",
+ fname, strerror(errno), errno);
+ off_t end = lseek(fd, 0, SEEK_END);
+
+ int nr = sizeof(prelink_info_t);
+
+ off_t sz = lseek(fd, -nr, SEEK_CUR);
+ ASSERT((long)(end - sz) == (long)nr);
+ FAILIF(sz == (off_t)-1,
+ "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+ fd, strerror(errno), errno);
+
+ prelink_info_t info;
+ int num_read = read(fd, &info, nr);
+ FAILIF(num_read < 0,
+ "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+ fd, strerror(errno), errno);
+ FAILIF(num_read != sizeof(info),
+ "read(%d, &info, sizeof(prelink_info_t)): did not read %d bytes as "
+ "expected (read %d)!\n",
+ fd, sizeof(info), num_read);
+
+ int prelinked = 0;
+ if (!strncmp(info.tag, "PRE ", 4)) {
+ set_prelink(prelink_addr, elf_little, &info);
+ prelinked = 1;
+ }
+ FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+ return prelinked;
+}
+
+#endif /*SUPPORT_ANDROID_PRELINK_TAGS*/
diff --git a/tools/isprelinked/prelink_info.h b/tools/isprelinked/prelink_info.h
new file mode 100644
index 000000000..afc03e92b
--- /dev/null
+++ b/tools/isprelinked/prelink_info.h
@@ -0,0 +1,8 @@
+#ifndef PRELINK_INFO_H
+#define PRELINK_INFO_H
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr);
+
+#endif
+#endif/*PRELINK_INFO_H*/
diff --git a/tools/kcm/Android.mk b/tools/kcm/Android.mk
new file mode 100644
index 000000000..130a13ac7
--- /dev/null
+++ b/tools/kcm/Android.mk
@@ -0,0 +1,15 @@
+# Copyright 2007 The Android Open Source Project
+#
+# Copies files into the directory structure described by a manifest
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ kcm.cpp
+
+LOCAL_MODULE := kcm
+
+include $(BUILD_HOST_EXECUTABLE)
+
+
diff --git a/tools/kcm/kcm.cpp b/tools/kcm/kcm.cpp
new file mode 100644
index 000000000..3e6320b65
--- /dev/null
+++ b/tools/kcm/kcm.cpp
@@ -0,0 +1,421 @@
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ui/KeycodeLabels.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <map>
+#include <string>
+#include <utils/ByteOrder.h>
+
+using namespace std;
+
+enum {
+ LENDIAN,
+ BENDIAN
+};
+
+/*
+ * 1: KeyEvent name
+ * 2: display_label
+ * 3: number
+ * 4..7: base, shift, alt, shift-alt
+ */
+#define COLUMNS (3+4)
+
+struct KeyRecord
+{
+ int lineno;
+ int values[COLUMNS];
+};
+
+struct PropValue
+{
+ PropValue() { lineno = -1; }
+ PropValue(const PropValue& that) { lineno=that.lineno; value=that.value; }
+ PropValue(int l, const string& v) { lineno = l; value = v; }
+
+ int lineno;
+ string value;
+};
+
+static int usage();
+
+// 0 -- ok
+// >0 -- error
+static int parse_key_line(const char* filename, int lineno, char* line,
+ KeyRecord* out);
+static int write_kr(int fd, const KeyRecord& kr);
+
+int g_endian;
+
+int
+main(int argc, char** argv)
+{
+ int err;
+ if (argc != 3) {
+ return usage();
+ }
+
+ const char* filename = argv[1];
+ const char* outfilename = argv[2];
+
+ int in = open(filename, O_RDONLY);
+ if (in == -1) {
+ fprintf(stderr, "kcm: error opening file for read: %s\n", filename);
+ return 1;
+ }
+
+ off_t size = lseek(in, 0, SEEK_END);
+ lseek(in, 0, SEEK_SET);
+
+ char* input = (char*)malloc(size+1);
+ read(in, input, size);
+ input[size] = '\0';
+
+ close(in);
+ in = -1;
+
+ map<string,PropValue> properties;
+ map<int,KeyRecord> keys;
+ int errorcount = 0;
+ int lineno = 1;
+ char *thisline = input;
+ while (*thisline) {
+ KeyRecord kr;
+ char *nextline = thisline;
+
+ while (*nextline != '\0' && *nextline != '\n' && *nextline != '\r') {
+ nextline++;
+ }
+
+ // eat whitespace, but not newlines
+ while (*thisline != '\0' && (*thisline == ' ' || *thisline == '\t')) {
+ thisline++;
+ }
+
+ // find the end of the line
+ char lineend = *nextline;
+ *nextline = '\0';
+ if (lineend == '\r' && nextline[1] == '\n') {
+ nextline++;
+ }
+
+ if (*thisline == '\0' || *thisline == '\r' || *thisline == '\n'
+ || *thisline == '#') {
+ // comment or blank line
+ }
+ else if (*thisline == '[') {
+ // property - syntax [name=value]
+ // look for =
+ char* prop = thisline+1;
+ char* end = prop;
+ while (*end != '\0' && *end != '=') {
+ end++;
+ }
+ if (*end != '=') {
+ fprintf(stderr, "%s:%d: invalid property line: %s\n",
+ filename, lineno, thisline);
+ errorcount++;
+ } else {
+ *end = '\0';
+ char* value = end+1;
+ end = nextline;
+ while (end > prop && *end != ']') {
+ end--;
+ }
+ if (*end != ']') {
+ fprintf(stderr, "%s:%d: property missing closing ]: %s\n",
+ filename, lineno, thisline);
+ errorcount++;
+ } else {
+ *end = '\0';
+ properties[prop] = PropValue(lineno, value);
+ }
+ }
+ }
+ else {
+ // key
+ err = parse_key_line(filename, lineno, thisline, &kr);
+ if (err == 0) {
+ kr.lineno = lineno;
+
+ map<int,KeyRecord>::iterator old = keys.find(kr.values[0]);
+ if (old != keys.end()) {
+ fprintf(stderr, "%s:%d: keycode %d already defined\n",
+ filename, lineno, kr.values[0]);
+ fprintf(stderr, "%s:%d: previously defined here\n",
+ filename, old->second.lineno);
+ errorcount++;
+ }
+
+ keys[kr.values[0]] = kr;
+ }
+ else if (err > 0) {
+ errorcount += err;
+ }
+ }
+ lineno++;
+
+ nextline++;
+ thisline = nextline;
+
+ if (errorcount > 20) {
+ fprintf(stderr, "%s:%d: too many errors. stopping.\n", filename,
+ lineno);
+ return 1;
+ }
+ }
+
+ free(input);
+
+ map<string,PropValue>::iterator sit = properties.find("type");
+ if (sit == properties.end()) {
+ fprintf(stderr, "%s: key character map must contain type property.\n",
+ argv[0]);
+ errorcount++;
+ }
+ PropValue pv = sit->second;
+ unsigned char kbdtype = 0;
+ if (pv.value == "NUMERIC") {
+ kbdtype = 1;
+ }
+ else if (pv.value == "Q14") {
+ kbdtype = 2;
+ }
+ else if (pv.value == "QWERTY") {
+ kbdtype = 3;
+ }
+ else {
+ fprintf(stderr, "%s:%d: keyboard type must be one of NUMERIC, Q14 "
+ " or QWERTY, not %s\n", filename, pv.lineno, pv.value.c_str());
+ }
+
+ if (errorcount != 0) {
+ return 1;
+ }
+
+ int out = open(outfilename, O_RDWR|O_CREAT|O_TRUNC, 0660);
+ if (out == -1) {
+ fprintf(stderr, "kcm: error opening file for write: %s\n", outfilename);
+ return 1;
+ }
+
+ int count = keys.size();
+
+ map<int,KeyRecord>::iterator it;
+ int n;
+
+ /**
+ * File Format:
+ * Offset Description Value
+ * 0 magic string "keychar"
+ * 8 endian marker 0x12345678
+ * 12 version 0x00000002
+ * 16 key count number of key entries
+ * 20 keyboard type NUMERIC, Q14, QWERTY, etc.
+ * 21 padding 0
+ * 32 the keys
+ */
+ err = write(out, "keychar", 8);
+ if (err == -1) goto bad_write;
+
+ n = htodl(0x12345678);
+ err = write(out, &n, 4);
+ if (err == -1) goto bad_write;
+
+ n = htodl(0x00000002);
+ err = write(out, &n, 4);
+ if (err == -1) goto bad_write;
+
+ n = htodl(count);
+ err = write(out, &n, 4);
+ if (err == -1) goto bad_write;
+
+ err = write(out, &kbdtype, 1);
+ if (err == -1) goto bad_write;
+
+ char zero[11];
+ memset(zero, 0, 11);
+ err = write(out, zero, 11);
+ if (err == -1) goto bad_write;
+
+ for (it = keys.begin(); it != keys.end(); it++) {
+ const KeyRecord& kr = it->second;
+ /*
+ printf("%2d/ [%d] [%d] [%d] [%d] [%d] [%d] [%d]\n", kr.lineno,
+ kr.values[0], kr.values[1], kr.values[2], kr.values[3],
+ kr.values[4], kr.values[5], kr.values[6]);
+ */
+ err = write_kr(out, kr);
+ if (err == -1) goto bad_write;
+ }
+
+ close(out);
+ return 0;
+
+bad_write:
+ fprintf(stderr, "kcm: fatal error writing to file: %s\n", outfilename);
+ close(out);
+ unlink(outfilename);
+ return 1;
+}
+
+static int usage()
+{
+ fprintf(stderr,
+ "usage: kcm INPUT OUTPUT\n"
+ "\n"
+ "INPUT keycharmap file\n"
+ "OUTPUT compiled keycharmap file\n"
+ );
+ return 1;
+}
+
+static int
+is_whitespace(const char* p)
+{
+ while (*p) {
+ if (!isspace(*p)) {
+ return 0;
+ }
+ p++;
+ }
+ return 1;
+}
+
+
+static int
+parse_keycode(const char* filename, int lineno, char* str, int* value)
+{
+ const KeycodeLabel *list = KEYCODES;
+ while (list->literal) {
+ if (0 == strcmp(str, list->literal)) {
+ *value = list->value;
+ return 0;
+ }
+ list++;
+ }
+
+ char* endptr;
+ *value = strtol(str, &endptr, 0);
+ if (*endptr != '\0') {
+ fprintf(stderr, "%s:%d: expected keycode label or number near: "
+ "%s\n", filename, lineno, str);
+ return 1;
+ }
+
+ if (*value == 0) {
+ fprintf(stderr, "%s:%d: 0 is not a valid keycode.\n",
+ filename, lineno);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+parse_number(const char* filename, int lineno, char* str, int* value)
+{
+ int len = strlen(str);
+
+ if (len == 3 && str[0] == '\'' && str[2] == '\'') {
+ if (str[1] > 0 && str[1] < 127) {
+ *value = (int)str[1];
+ return 0;
+ } else {
+ fprintf(stderr, "%s:%d: only low ascii characters are allowed in"
+ " quotes near: %s\n", filename, lineno, str);
+ return 1;
+ }
+ }
+
+ char* endptr;
+ *value = strtol(str, &endptr, 0);
+ if (*endptr != '\0') {
+ fprintf(stderr, "%s:%d: expected number or quoted ascii but got: %s\n",
+ filename, lineno, str);
+ return 1;
+ }
+
+ if (*value >= 0xfffe || *value < 0) {
+ fprintf(stderr, "%s:%d: unicode char out of range (no negatives, "
+ "nothing larger than 0xfffe): %s\n", filename, lineno, str);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+parse_key_line(const char* filename, int lineno, char* line, KeyRecord* out)
+{
+ char* p = line;
+
+ int len = strlen(line);
+ char* s[COLUMNS];
+ for (int i=0; i<COLUMNS; i++) {
+ s[i] = (char*)malloc(len+1);
+ }
+
+ for (int i = 0; i < COLUMNS; i++) {
+ while (*p != '\0' && isspace(*p)) {
+ p++;
+ }
+
+ if (*p == '\0') {
+ fprintf(stderr, "%s:%d: not enough on this line: %s\n", filename,
+ lineno, line);
+ return 1;
+ }
+
+ char *p1 = p;
+ while (*p != '\0' && !isspace(*p)) {
+ p++;
+ }
+
+ memcpy(s[i], p1, p - p1);
+ s[i][p - p1] = '\0';
+ }
+
+ while (*p != '\0' && isspace(*p)) {
+ *p++;
+ }
+ if (*p != '\0') {
+ fprintf(stderr, "%s:%d: too much on one line near: %s\n", filename,
+ lineno, p);
+ fprintf(stderr, "%s:%d: -->%s<--\n", filename, lineno, line);
+ return 1;
+ }
+
+ int errorcount = parse_keycode(filename, lineno, s[0], &out->values[0]);
+ for (int i=1; i<COLUMNS && errorcount == 0; i++) {
+ errorcount += parse_number(filename, lineno, s[i], &out->values[i]);
+ }
+
+ return errorcount;
+}
+
+struct WrittenRecord
+{
+ unsigned int keycode; // 4 bytes
+ unsigned short values[COLUMNS - 1]; // 6*2 bytes = 12
+ // 16 bytes total
+};
+
+static int
+write_kr(int fd, const KeyRecord& kr)
+{
+ WrittenRecord wr;
+
+ wr.keycode = htodl(kr.values[0]);
+ for (int i=0; i<COLUMNS - 1; i++) {
+ wr.values[i] = htods(kr.values[i+1]);
+ }
+
+ return write(fd, &wr, sizeof(WrittenRecord));
+}
+
diff --git a/tools/lsd/Android.mk b/tools/lsd/Android.mk
new file mode 100644
index 000000000..a22474160
--- /dev/null
+++ b/tools/lsd/Android.mk
@@ -0,0 +1,43 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for lsd
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DBIG_ENDIAN=1
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DSUPPORT_ANDROID_PRELINK_TAGS
+LOCAL_CFLAGS += -DDEBUG
+
+ifeq ($(HOST_OS),windows)
+LOCAL_LDLIBS += -lintl
+endif
+
+LOCAL_SRC_FILES := \
+ cmdline.c \
+ debug.c \
+ hash.c \
+ lsd.c \
+ main.c
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ external/elfutils/lib/ \
+ external/elfutils/libelf/ \
+ external/elfutils/libebl/
+
+LOCAL_STATIC_LIBRARIES := libelf libebl libebl_arm #dl
+
+LOCAL_MODULE := lsd
+
+include $(BUILD_HOST_EXECUTABLE)
+endif #TARGET_ARCH==arm
+
diff --git a/tools/lsd/cmdline.c b/tools/lsd/cmdline.c
new file mode 100644
index 000000000..a3445cd9b
--- /dev/null
+++ b/tools/lsd/cmdline.c
@@ -0,0 +1,130 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] = {
+ {"verbose", no_argument, 0, 'V'},
+ {"help", no_argument, 0, 'h'},
+ {"print-info", no_argument, 0, 'p'},
+ {"list-needed-libs", no_argument, 0, 'n'},
+ {"lookup", required_argument, 0, 'L'},
+ {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static const char *descriptions[] = {
+ "print verbose output",
+ "print help screen",
+ "for each file, generate a listing of all dependencies that each symbol "
+ "satisfies",
+ "print out a list of needed libraries",
+ "provide a directory for library lookup"
+};
+
+void print_help(void)
+{
+ fprintf(stdout,
+ "invokation:\n"
+ "\tlsd file1 [file2 file3 ... fileN] [-Ldir1 -Ldir2 ... -LdirN] "
+ "[-Vpn]\n"
+ "or\n"
+ "\tlsd -h\n\n");
+ fprintf(stdout, "options:\n");
+ struct option *opt = long_options;
+ const char **desc = descriptions;
+ while (opt->name) {
+ fprintf(stdout, "\t-%c\n"
+ "\t--%-15s: %s\n",
+ opt->val,
+ opt->name,
+ *desc);
+ opt++;
+ desc++;
+ }
+}
+
+int get_options(int argc, char **argv,
+ int *list_needed_libs,
+ int *info,
+ char ***dirs,
+ int *num_dirs,
+ int *verbose)
+{
+ int c;
+
+ ASSERT(list_needed_libs);
+ *list_needed_libs = 0;
+ ASSERT(info);
+ *info = 0;
+ ASSERT(verbose);
+ *verbose = 0;
+ ASSERT(dirs);
+ *dirs = NULL;
+ ASSERT(num_dirs);
+ int size = 0;
+ *num_dirs = 0;
+
+ while (1) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv,
+ "VhpnL:",
+ long_options,
+ &option_index);
+ /* Detect the end of the options. */
+ if (c == -1) break;
+
+ if (isgraph(c)) {
+ INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+ }
+
+#define SET_STRING_OPTION(name) do { \
+ ASSERT(optarg); \
+ (*name) = strdup(optarg); \
+} while(0)
+
+ switch (c) {
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ if (long_options[option_index].flag != 0)
+ break;
+ INFO ("option %s", long_options[option_index].name);
+ if (optarg)
+ INFO (" with arg %s", optarg);
+ INFO ("\n");
+ break;
+ case 'h': print_help(); exit(1); break;
+ case 'V': *verbose = 1; break;
+ case 'p': *info = 1; break;
+ case 'n': *list_needed_libs = 1; break;
+ case 'L':
+ {
+ if (*num_dirs == size) {
+ size += 10;
+ *dirs = (char **)REALLOC(*dirs, size * sizeof(char *));
+ }
+ SET_STRING_OPTION(((*dirs) + *num_dirs));
+ (*num_dirs)++;
+ }
+ break;
+ case '?':
+ /* getopt_long already printed an error message. */
+ break;
+
+#undef SET_STRING_OPTION
+
+ default:
+ FAILIF(1, "Unknown option");
+ }
+ }
+
+ return optind;
+}
diff --git a/tools/lsd/cmdline.h b/tools/lsd/cmdline.h
new file mode 100644
index 000000000..fc3be3e3b
--- /dev/null
+++ b/tools/lsd/cmdline.h
@@ -0,0 +1,13 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(void);
+
+int get_options(int argc, char **argv,
+ int *list_needed_libs,
+ int *info,
+ char ***dirs,
+ int *num_dirs,
+ int *verbose);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/lsd/common.h b/tools/lsd/common.h
new file mode 100644
index 000000000..6447b10db
--- /dev/null
+++ b/tools/lsd/common.h
@@ -0,0 +1,12 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+#endif/*COMMON_H*/
diff --git a/tools/lsd/debug.c b/tools/lsd/debug.c
new file mode 100644
index 000000000..54b18df44
--- /dev/null
+++ b/tools/lsd/debug.c
@@ -0,0 +1,39 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#define NUM_COLS (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize)
+{
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ }
+ else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n", i);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
+
diff --git a/tools/lsd/debug.h b/tools/lsd/debug.h
new file mode 100644
index 000000000..f7842f89e
--- /dev/null
+++ b/tools/lsd/debug.h
@@ -0,0 +1,93 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+#define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+#define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+#define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+#define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size)
+{
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size)
+{
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size)
+{
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr)
+{
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr)
+{
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR(x...) fprintf(stderr, ##x)
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/lsd/hash.c b/tools/lsd/hash.c
new file mode 100644
index 000000000..bbac6751b
--- /dev/null
+++ b/tools/lsd/hash.c
@@ -0,0 +1,29 @@
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <hash.h>
+#include <string.h>
+
+int hash_lookup(Elf *elf,
+ Elf_Data *hash,
+ Elf_Data *symtab,
+ Elf_Data *symstr,
+ const char *symname)
+{
+ Elf32_Word *hash_data = (Elf32_Word *)hash->d_buf;
+ Elf32_Word index;
+ Elf32_Word nbuckets = *hash_data++;
+ Elf32_Word *buckets = ++hash_data;
+ Elf32_Word *chains = hash_data + nbuckets;
+
+ index = buckets[elf_hash(symname) % nbuckets];
+ while(index != STN_UNDEF &&
+ strcmp((char *)symstr->d_buf +
+ ((Elf32_Sym *)symtab->d_buf)[index].st_name,
+ symname))
+ {
+ index = chains[index];
+ }
+
+ return index;
+}
diff --git a/tools/lsd/hash.h b/tools/lsd/hash.h
new file mode 100644
index 000000000..af29b9e04
--- /dev/null
+++ b/tools/lsd/hash.h
@@ -0,0 +1,14 @@
+#ifndef HASH_H
+#define HASH_H
+
+#include <common.h>
+#include <libelf.h>
+#include <gelf.h>
+
+int hash_lookup(Elf *elf,
+ Elf_Data *hash,
+ Elf_Data *symtab,
+ Elf_Data *symstr,
+ const char *symname);
+
+#endif/*HASH_H*/
diff --git a/tools/lsd/lsd.c b/tools/lsd/lsd.c
new file mode 100644
index 000000000..03c235b93
--- /dev/null
+++ b/tools/lsd/lsd.c
@@ -0,0 +1,777 @@
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <libebl.h>
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <hash.h>
+#include <lsd.h>
+
+extern int verbose_flag;
+
+typedef struct source_t source_t;
+
+typedef struct {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+ Elf_Data *data;
+} section_info_t;
+
+typedef struct next_export_t {
+ source_t *source;
+ int next_idx;
+} next_export_t;
+
+struct source_t {
+ source_t *next;
+ int visited;
+
+ char *name; /* full path name of this executable file */
+ /* ELF-related information: */
+ Elf *elf;
+ int elf_fd;
+ GElf_Ehdr elf_hdr;
+ size_t shstrndx;
+ int shnum; /* number of sections */
+
+ section_info_t symtab;
+ section_info_t strtab;
+ section_info_t dynamic;
+ section_info_t hash;
+
+ section_info_t *relocations;
+ int num_relocations; /* number of relocs (<= relocations_size) */
+ int relocations_size; /* sice of array -- NOT number of relocs! */
+
+ /* satisfied_execs: array containing pointers to the libraries or
+ executables that this executable satisfies symbol references for. */
+ source_t **satisfied_execs;
+ int num_satisfied_execs;
+ int satisfied_execs_size;
+
+ /* satisfied: array is parallel to symbol table; for each undefined symbol
+ in that array, we maintain a flag stating whether that symbol has been
+ satisfied, and if so, by which library. This applies both to executable
+ files and libraries.
+ */
+ source_t **satisfied;
+
+ /* exports: array is parallel to symbol table; for each global symbol
+ in that array, we maintain a flag stating whether that symbol satisfies
+ a dependency in some other file. num_syms is the length of the exports
+ array, as well as the satisfied array. This applied to libraries only.
+
+ next_exports: this is a bit tricky. We use this field to maintain a
+ linked list of source_t for each global symbol of a shared library.
+ For a shared library's global symbol at index N has the property that
+ exports[N] is the head of a linked list (threaded through next_export)
+ of all source_t that this symbol resolves a reference to. For example,
+ if symbol printf has index 1000 in libc.so, and an executable A and
+ library L use printf, then the source_t entry corresponding to libc.so
+ will have exports[1000] be a linked list that contains the nodes for
+ application A and library L.
+ */
+
+ next_export_t *exports;
+ /* num_exported is the number of symbols in this file actually used by
+ somebody else; it's not the size of the exports array. */
+ int num_exported;
+ next_export_t *next_export;
+ int num_next_export;
+ int next_export_size;
+
+ int num_syms; /* number of symbols in symbol table. This is the length of
+ both exports[] and satisfied[] arrays. */
+
+ /* This is an array that contains one element for each library dependency
+ listed in the executable or shared library. */
+ source_t **lib_deps; /* list of library dependencies */
+ int num_lib_deps; /* actual number of library dependencies */
+ int lib_deps_size; /* size of lib_deps array--NOT actual number of deps! */
+
+};
+
+static source_t *sources = NULL;
+
+static char * find_file(const char *libname,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs);
+
+static inline source_t* find_source(const char *name,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs) {
+ source_t *trav = sources;
+ char *full = find_file(name, lib_lookup_dirs, num_lib_lookup_dirs);
+ FAILIF(full == NULL, "Cannot construct full path for file [%s]!\n", name);
+ while (trav) {
+ if (!strcmp(trav->name, full))
+ break;
+ trav = trav->next;
+ }
+ free(full);
+ return trav;
+}
+
+static inline void add_to_sources(source_t *src) {
+ src->next = sources;
+ sources = src;
+}
+
+static source_t* init_source(char *full_path) {
+ source_t *source = (source_t *)CALLOC(1, sizeof(source_t));
+
+ ASSERT(full_path);
+ source->name = full_path;
+ source->elf_fd = -1;
+
+ INFO("Opening %s...\n", full_path);
+ source->elf_fd = open(full_path, O_RDONLY);
+ FAILIF(source->elf_fd < 0, "open(%s): %s (%d)\n",
+ full_path,
+ strerror(errno),
+ errno);
+ INFO("Calling elf_begin(%s)...\n", full_path);
+ source->elf = elf_begin(source->elf_fd, ELF_C_READ, NULL);
+ FAILIF_LIBELF(source->elf == NULL, elf_begin);
+
+ /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
+ if (elf_kind(source->elf) != ELF_K_ELF) {
+ ERROR("Input file %s is not in ELF format!\n", full_path);
+ return NULL;
+ }
+
+ /* Make sure this is a shared library or an executable. */
+ {
+ INFO("Making sure %s is a shared library or an executable...\n",
+ full_path);
+ FAILIF_LIBELF(0 == gelf_getehdr(source->elf, &source->elf_hdr), gelf_getehdr);
+ FAILIF(source->elf_hdr.e_type != ET_DYN &&
+ source->elf_hdr.e_type != ET_EXEC,
+ "%s must be a shared library (elf type is %d, expecting %d).\n",
+ full_path,
+ source->elf_hdr.e_type,
+ ET_DYN);
+ }
+
+ /* Get the index of the section-header-strings-table section. */
+ FAILIF_LIBELF(elf_getshstrndx (source->elf, &source->shstrndx) < 0,
+ elf_getshstrndx);
+
+ FAILIF_LIBELF(elf_getshnum (source->elf, &source->shnum) < 0, elf_getshnum);
+
+ /* Find various sections. */
+ size_t scnidx;
+ Elf_Scn *scn;
+ GElf_Shdr *shdr, shdr_mem;
+ INFO("Locating %d sections in %s...\n", source->shnum, full_path);
+ for (scnidx = 1; scnidx < source->shnum; scnidx++) {
+ scn = elf_getscn(source->elf, scnidx);
+ FAILIF_LIBELF(NULL == scn, elf_getscn);
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
+ INFO("\tfound section [%s]...\n", elf_strptr(source->elf, source->shstrndx, shdr->sh_name));
+ if (shdr->sh_type == SHT_DYNSYM) {
+ source->symtab.scn = scn;
+ source->symtab.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
+ memcpy(&source->symtab.shdr, shdr, sizeof(GElf_Shdr));
+
+ /* The sh_link field of the section header of the symbol table
+ contains the index of the associated strings table. */
+ source->strtab.scn = elf_getscn(source->elf,
+ source->symtab.shdr.sh_link);
+ FAILIF_LIBELF(NULL == source->strtab.scn, elf_getscn);
+ FAILIF_LIBELF(NULL == gelf_getshdr(scn, &source->strtab.shdr),
+ gelf_getshdr);
+ source->strtab.data = elf_getdata(source->strtab.scn, NULL);
+ FAILIF_LIBELF(NULL == source->strtab.data, elf_getdata);
+ }
+ else if (shdr->sh_type == SHT_DYNAMIC) {
+ source->dynamic.scn = scn;
+ source->dynamic.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->symtab.data, elf_getdata);
+ memcpy(&source->dynamic.shdr, shdr, sizeof(GElf_Shdr));
+ }
+ else if (shdr->sh_type == SHT_HASH) {
+ source->hash.scn = scn;
+ source->hash.data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == source->hash.data, elf_getdata);
+ memcpy(&source->hash.shdr, shdr, sizeof(GElf_Shdr));
+ }
+ else if (shdr->sh_type == SHT_REL || shdr->sh_type == SHT_RELA) {
+ if (source->num_relocations == source->relocations_size) {
+ source->relocations_size += 5;
+ source->relocations =
+ (section_info_t *)REALLOC(source->relocations,
+ source->relocations_size *
+ sizeof(section_info_t));
+ }
+ section_info_t *reloc =
+ source->relocations + source->num_relocations;
+ reloc->scn = scn;
+ reloc->data = elf_getdata(scn, NULL);
+ FAILIF_LIBELF(NULL == reloc->data, elf_getdata);
+ memcpy(&reloc->shdr, shdr, sizeof(GElf_Shdr));
+ source->num_relocations++;
+ }
+ }
+
+ if (source->dynamic.scn == NULL) {
+ INFO("File [%s] does not have a dynamic section!\n", full_path);
+ return 0;
+ }
+
+ FAILIF(source->symtab.scn == NULL,
+ "File [%s] does not have a dynamic symbol table!\n",
+ full_path);
+
+ FAILIF(source->hash.scn == NULL,
+ "File [%s] does not have a hash table!\n",
+ full_path);
+ FAILIF(source->hash.shdr.sh_link != elf_ndxscn(source->symtab.scn),
+ "Hash points to section %d, not to %d as expected!\n",
+ source->hash.shdr.sh_link,
+ elf_ndxscn(scn));
+
+ /* Now, find out how many symbols we have and allocate the array of
+ satisfied symbols.
+
+ NOTE: We don't count the number of undefined symbols here; we will
+ iterate over the symbol table later, and count them then, when it is
+ more convenient.
+ */
+ size_t symsize = gelf_fsize (source->elf,
+ ELF_T_SYM,
+ 1, source->elf_hdr.e_version);
+ ASSERT(symsize);
+
+ source->num_syms = source->symtab.data->d_size / symsize;
+ source->satisfied = (source_t **)CALLOC(source->num_syms,
+ sizeof(source_t *));
+ source->exports = (source_t **)CALLOC(source->num_syms,
+ sizeof(next_export_t));
+
+ source->num_exported = 0;
+ source->satisfied_execs = NULL;
+ source->num_satisfied_execs = 0;
+ source->satisfied_execs_size = 0;
+
+ add_to_sources(source);
+ return source;
+}
+
+static void destroy_source(source_t *source) {
+ FREE(source->satisfied_execs);
+ FREE(source->satisfied);
+ FREE(source->exports);
+ FREE(source->next_export);
+ FREE(source->lib_deps); /* list of library dependencies */
+ FAILIF_LIBELF(elf_end(source->elf), elf_end);
+ FAILIF(close(source->elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+ source->name, strerror(errno), errno);
+ FREE(source->name);
+ FREE(source);
+}
+
+static void print_needed_libs(source_t *source)
+{
+ size_t idx;
+ for (idx = 0; idx < source->num_lib_deps; idx++) {
+ PRINT("%s:%s\n",
+ source->name,
+ source->lib_deps[idx]->name);
+ }
+}
+
+static int is_symbol_imported(source_t *source,
+ GElf_Sym *sym,
+ size_t symidx)
+{
+ const char *symname = elf_strptr(source->elf,
+ elf_ndxscn(source->strtab.scn),
+ sym->st_name);
+
+ /* A symbol is imported by an executable or a library if it is undefined
+ and is either global or weak. There is an additional case for
+ executables that we will check below. */
+ if (sym->st_shndx == SHN_UNDEF &&
+ (GELF_ST_BIND(sym->st_info) == STB_GLOBAL ||
+ GELF_ST_BIND(sym->st_info) == STB_WEAK)) {
+ INFO("*** symbol [%s:%s] is imported (UNDEFIEND).\n",
+ source->name,
+ symname);
+ return 1;
+ }
+
+#ifdef ARM_SPECIFIC_HACKS
+ /* A symbol is imported by an executable if is marked as an undefined
+ symbol--this is standard to all ELF formats. Alternatively, according
+ to the ARM specifications, a symbol in a BSS section that is also marked
+ by an R_ARM_COPY relocation is also imported. */
+
+ if (source->elf_hdr.e_type != ET_EXEC) {
+ INFO("is_symbol_imported(): [%s] is a library, "
+ "no further checks.\n", source->name);
+ return 0;
+ }
+
+ /* Is the symbol in the BSS section, and is there a COPY relocation on
+ that symbol? */
+ INFO("*** [%s:%s] checking further to see if symbol is imported.\n",
+ source->name, symname);
+ if (sym->st_shndx < source->shnum) {
+ /* Is it the .bss section? */
+ Elf_Scn *scn = elf_getscn(source->elf, sym->st_shndx);
+ FAILIF_LIBELF(NULL == scn, elf_getscn);
+ GElf_Shdr *shdr, shdr_mem;
+ shdr = gelf_getshdr(scn, &shdr_mem);
+ FAILIF_LIBELF(NULL == shdr, gelf_getshdr);
+ if (!strcmp(".bss", elf_strptr(source->elf,
+ source->shstrndx,
+ shdr->sh_name)))
+ {
+ /* Is there an R_ARM_COPY relocation on this symbol? Iterate
+ over the list of relocation sections and scan each section for
+ an entry that matches the symbol. */
+ size_t idx;
+ for (idx = 0; idx < source->num_relocations; idx++) {
+ section_info_t *reloc = source->relocations + idx;
+ /* Does the relocation section refer to the symbol table in
+ which this symbol resides, and does it relocate the .bss
+ section? */
+ if (reloc->shdr.sh_link == elf_ndxscn(source->symtab.scn) &&
+ reloc->shdr.sh_info == sym->st_shndx)
+ {
+ /* Go over the relocations and see if any of them matches
+ our symbol. */
+ size_t nrels = reloc->shdr.sh_size / reloc->shdr.sh_entsize;
+ size_t relidx, newidx;
+ if (reloc->shdr.sh_type == SHT_REL) {
+ for (newidx = relidx = 0; relidx < nrels; ++relidx) {
+ GElf_Rel rel_mem;
+ FAILIF_LIBELF(gelf_getrel (reloc->data,
+ relidx,
+ &rel_mem) == NULL,
+ gelf_getrel);
+ if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
+ GELF_R_SYM (rel_mem.r_info) == symidx)
+ {
+ INFO("*** symbol [%s:%s] is imported "
+ "(DEFINED, REL-COPY-RELOCATED).\n",
+ source->name,
+ symname);
+ return 1;
+ }
+ } /* for each rel entry... */
+ } else {
+ for (newidx = relidx = 0; relidx < nrels; ++relidx) {
+ GElf_Rela rel_mem;
+ FAILIF_LIBELF(gelf_getrela (reloc->data,
+ relidx,
+ &rel_mem) == NULL,
+ gelf_getrela);
+ if (GELF_R_TYPE(rel_mem.r_info) == R_ARM_COPY &&
+ GELF_R_SYM (rel_mem.r_info) == symidx)
+ {
+ INFO("*** symbol [%s:%s] is imported "
+ "(DEFINED, RELA-COPY-RELOCATED).\n",
+ source->name,
+ symname);
+ return 1;
+ }
+ } /* for each rela entry... */
+ } /* if rel else rela */
+ }
+ }
+ }
+ }
+#endif/*ARM_SPECIFIC_HACKS*/
+
+ return 0;
+}
+
+static void resolve(source_t *source) {
+ /* Iterate the symbol table. For each undefined symbol, scan the
+ list of dependencies till we find a global symbol in one of them that
+ satisfies the undefined reference. At this point, we update both the
+ satisfied[] array of the sources entry, as well as the exports array of
+ the dependency where we found the match.
+ */
+
+ GElf_Sym *sym, sym_mem;
+ size_t symidx;
+ for (symidx = 0; symidx < source->num_syms; symidx++) {
+ sym = gelf_getsymshndx(source->symtab.data,
+ NULL,
+ symidx,
+ &sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ if (is_symbol_imported(source, sym, symidx))
+ {
+ /* This is an undefined symbol. Go over the list of libraries
+ and look it up. */
+ size_t libidx;
+ int found = 0;
+ source_t *last_found = NULL;
+ const char *symname = elf_strptr(source->elf,
+ elf_ndxscn(source->strtab.scn),
+ sym->st_name);
+ for (libidx = 0; libidx < source->num_lib_deps; libidx++) {
+ source_t *lib = source->lib_deps[libidx];
+ int lib_symidx = hash_lookup(lib->elf,
+ lib->hash.data,
+ lib->symtab.data,
+ lib->strtab.data,
+ symname);
+ if (STN_UNDEF != lib_symidx)
+ {
+ /* We found the symbol--now check to see if it is global
+ or weak. If this is the case, then the symbol satisfies
+ the dependency. */
+ GElf_Sym *lib_sym, lib_sym_mem;
+ lib_sym = gelf_getsymshndx(lib->symtab.data,
+ NULL,
+ lib_symidx,
+ &lib_sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == lib_sym, gelf_getsymshndx);
+
+ if(lib_sym->st_shndx != STN_UNDEF &&
+ (GELF_ST_BIND(lib_sym->st_info) == STB_GLOBAL ||
+ GELF_ST_BIND(lib_sym->st_info) == STB_WEAK))
+ {
+ /* We found the symbol! Update the satisfied array at this
+ index location. */
+ source->satisfied[symidx] = lib;
+ /* Now, link this structure into the linked list
+ corresponding to the found symbol in the library's
+ global array. */
+ if (source->num_next_export == source->next_export_size) {
+ source->next_export_size += 30;
+ source->next_export =
+ (source_t **)REALLOC(source->next_export,
+ source->next_export_size *
+ sizeof(struct next_export_t));
+ }
+ source->next_export[source->num_next_export] = lib->exports[lib_symidx];
+ lib->exports[lib_symidx].source = source;
+ lib->exports[lib_symidx].next_idx = source->num_next_export;
+
+ source->num_next_export++;
+ lib->num_exported++;
+
+ INFO("[%s:%s (index %d)] satisfied by [%s] (index %d)\n",
+ source->name,
+ symname,
+ symidx,
+ lib->name,
+ lib_symidx);
+ if (found) {
+ if (found == 1) {
+ found++;
+ ERROR("ERROR: multiple definitions found for [%s:%s]!\n",
+ source->name, symname);
+ ERROR("\tthis definition [%s]\n", lib->name);
+ }
+ ERROR("\tprevious definition [%s]\n", last_found->name);
+ }
+
+ last_found = lib;
+ if (!found) found = 1;
+ }
+ }
+ }
+ if(found == 0) {
+ ERROR("ERROR: could not find match for %s:%s.\n",
+ source->name,
+ symname);
+ }
+ } /* if we found the symbol... */
+ } /* for each symbol... */
+} /* resolve() */
+
+static void print_used_symbols(source_t *source) {
+
+ int name_len = strlen(source->name);
+ static const char ext[] = ".syms";
+ char *filter = (char *)MALLOC(name_len + sizeof(ext));
+ strcpy(filter, source->name);
+ strcpy(filter + name_len, ext);
+
+ FILE *fp = fopen(filter, "w+");
+ FAILIF(NULL == fp,
+ "Can't open %s: %s (%d)\n",
+ filter,
+ strerror(errno), errno);
+
+ /* Is anybody using the symbols defined in source? */
+
+ if (source->num_exported > 0) {
+ INFO("[%s] exports %d symbols to %d libraries and executables.\n",
+ source->name,
+ source->num_exported,
+ source->num_satisfied_execs);
+ size_t symidx;
+ for (symidx = 0; symidx < source->num_syms; symidx++) {
+ if (source->exports[symidx].source != NULL) {
+ GElf_Sym *sym, sym_mem;
+ sym = gelf_getsymshndx(source->symtab.data,
+ NULL,
+ symidx,
+ &sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ fprintf(fp, "%s\n", elf_strptr(source->elf,
+ elf_ndxscn(source->strtab.scn),
+ sym->st_name));
+ }
+ }
+ }
+ else if (source->num_satisfied_execs > 0) {
+
+ /* Is the source listed as a depenency on anyone? If so, then the source exports no symbols
+ to anyone, but someone lists it as a dependency, which is unnecessary, so we print a warning.
+ */
+
+ ERROR("WARNING: [%s] is listed as a dependency in: ", source->name);
+ int i;
+ for (i = 0; i < source->num_satisfied_execs; i++) {
+ ERROR(" [%s],", source->satisfied_execs[i]->name);
+ }
+ ERROR(" but none of its symbols are used!.\n");
+ }
+#if 0 /* This is not really an error--a library's symbols may not be used anyone as specified in the ELF file,
+ but someone may still open a library via dlopen().
+ */
+ else {
+ ERROR("WARNING: None of [%s]'s symbols are used by any library or executable!\n", source->name);
+ }
+#endif
+
+ fclose(fp);
+ FREE(filter);
+}
+
+static void print_symbol_references(source_t *source) {
+
+ int name_len = strlen(source->name);
+ static const char ext[] = ".info";
+ char *filter = (char *)MALLOC(name_len + sizeof(ext));
+ strcpy(filter, source->name);
+ strcpy(filter + name_len, ext);
+
+ FILE *fp = fopen(filter, "w+");
+ FAILIF(NULL == fp,
+ "Can't open %s: %s (%d)\n",
+ filter,
+ strerror(errno), errno);
+
+ if (source->num_exported > 0) {
+ size_t symidx;
+ for (symidx = 0; symidx < source->num_syms; symidx++) {
+ if (source->exports[symidx].source != NULL) {
+ const char *symname;
+ GElf_Sym *sym, sym_mem;
+ sym = gelf_getsymshndx(source->symtab.data,
+ NULL,
+ symidx,
+ &sym_mem,
+ NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ symname = elf_strptr(source->elf,
+ elf_ndxscn(source->strtab.scn),
+ sym->st_name);
+ fprintf(fp, "%s\n", symname);
+ next_export_t *export = &source->exports[symidx];
+ while (export->source != NULL) {
+ //fprintf(stderr, "%s:%s\n", symname, export->source->name);
+ fprintf(fp, "\t%s\n", export->source->name);
+ export = &export->source->next_export[export->next_idx];
+ }
+ }
+ }
+ }
+
+ fclose(fp);
+ FREE(filter);
+}
+
+static char * find_file(const char *libname,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs) {
+ if (libname[0] == '/') {
+ /* This is an absolute path name--just return it. */
+ INFO("ABSOLUTE PATH: [%s].\n", libname);
+ return strdup(libname);
+ } else {
+ /* First try the working directory. */
+ int fd;
+ if ((fd = open(libname, O_RDONLY)) > 0) {
+ close(fd);
+ INFO("FOUND IN CURRENT DIR: [%s].\n", libname);
+ return strdup(libname);
+ } else {
+ /* Iterate over all library paths. For each path, append the file
+ name and see if there is a file at that place. If that fails,
+ bail out. */
+
+ char *name;
+ while (num_lib_lookup_dirs--) {
+ size_t lib_len = strlen(*lib_lookup_dirs);
+ /* one extra character for the slash, and another for the
+ terminating NULL. */
+ name = (char *)MALLOC(lib_len + strlen(libname) + 2);
+ strcpy(name, *lib_lookup_dirs);
+ name[lib_len] = '/';
+ strcpy(name + lib_len + 1, libname);
+ if ((fd = open(name, O_RDONLY)) > 0) {
+ close(fd);
+ INFO("FOUND: [%s] in [%s].\n", libname, name);
+ return name;
+ }
+ INFO("NOT FOUND: [%s] in [%s].\n", libname, name);
+ free(name);
+ }
+ }
+ }
+ return NULL;
+}
+
+static source_t* process_library(const char *libname,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs) {
+ source_t *source = find_source(libname, lib_lookup_dirs, num_lib_lookup_dirs);
+ if (NULL == source) {
+ INFO("Processing [%s].\n", libname);
+ char *full = find_file(libname, lib_lookup_dirs, num_lib_lookup_dirs);
+ FAILIF(NULL == full,
+ "Could not find [%s] in the current directory or in any of "
+ "the search paths!\n", libname);
+ source = init_source(full);
+ if (source) {
+ GElf_Dyn *dyn, dyn_mem;
+ size_t dynidx;
+ size_t numdyn =
+ source->dynamic.shdr.sh_size /
+ source->dynamic.shdr.sh_entsize;
+
+ for (dynidx = 0; dynidx < numdyn; dynidx++) {
+ dyn = gelf_getdyn (source->dynamic.data,
+ dynidx,
+ &dyn_mem);
+ FAILIF_LIBELF(NULL == dyn, gelf_getdyn);
+ if (dyn->d_tag == DT_NEEDED) {
+ /* Process the needed library recursively. */
+ const char *dep_lib =
+ elf_strptr (source->elf,
+ source->dynamic.shdr.sh_link,
+ dyn->d_un.d_val);
+ INFO("[%s] depends on [%s].\n", libname, dep_lib);
+ source_t *dep = process_library(dep_lib,
+ lib_lookup_dirs,
+ num_lib_lookup_dirs);
+
+ /* Tell dep that source depends on it. */
+ if (dep->num_satisfied_execs == dep->satisfied_execs_size) {
+ dep->satisfied_execs_size += 10;
+ dep->satisfied_execs =
+ REALLOC(dep->satisfied_execs,
+ dep->satisfied_execs_size *
+ sizeof(source_t *));
+ }
+ dep->satisfied_execs[dep->num_satisfied_execs++] = source;
+
+ /* Add the library to the dependency list. */
+ if (source->num_lib_deps == source->lib_deps_size) {
+ source->lib_deps_size += 10;
+ source->lib_deps = REALLOC(source->lib_deps,
+ source->lib_deps_size *
+ sizeof(source_t *));
+ }
+ source->lib_deps[source->num_lib_deps++] = dep;
+ }
+ } /* for each dynamic entry... */
+ }
+ } else INFO("[%s] has been processed already.\n", libname);
+
+ return source;
+}
+
+void lsd(char **execs, int num_execs,
+ int list_needed_libs,
+ int print_info,
+ char **lib_lookup_dirs, int num_lib_lookup_dirs) {
+
+ source_t *source; /* for general usage */
+ int input_idx;
+
+ for (input_idx = 0; input_idx < num_execs; input_idx++) {
+ INFO("executable: [%s]\n", execs[input_idx]);
+ /* Here process library is actually processing the top-level executable
+ files. */
+ process_library(execs[input_idx], lib_lookup_dirs, num_lib_lookup_dirs);
+ /* if source is NULL, then the respective executable is static */
+ /* Mark the source as an executable */
+ } /* for each input executable... */
+
+ if (list_needed_libs) {
+ source = sources;
+ while (source) {
+ print_needed_libs(source);
+ source = source->next;
+ }
+ }
+
+ /* Now, for each entry in the sources array, iterate its symbol table. For
+ each undefined symbol, scan the list of dependencies till we find a
+ global symbol in one of them that satisfies the undefined reference.
+ At this point, we update both the satisfied[] array of the sources entry,
+ as well as the exports array of the dependency where we found the match.
+ */
+
+ source = sources;
+ while (source) {
+ resolve(source);
+ source = source->next;
+ }
+
+ /* We are done! Since the end result of our calculations is a set of
+ symbols for each library that other libraries or executables link
+ against, we iterate over the set of libraries one last time, and for
+ each symbol that is marked as satisfying some dependence, we emit
+ a line with the symbol's name to a text file derived from the library's
+ name by appending the suffix .syms to it. */
+
+ source = sources;
+ while (source) {
+ /* If it's a library, print the results. */
+ if (source->elf_hdr.e_type == ET_DYN) {
+ print_used_symbols(source);
+ if (print_info)
+ print_symbol_references(source);
+ }
+ source = source->next;
+ }
+
+ /* Free the resources--you can't do it in the loop above because function
+ print_symbol_references() accesses nodes other than the one being
+ iterated over.
+ */
+ source = sources;
+ while (source) {
+ source_t *old = source;
+ source = source->next;
+ /* Destroy the evidence. */
+ destroy_source(old);
+ }
+}
+
diff --git a/tools/lsd/lsd.h b/tools/lsd/lsd.h
new file mode 100644
index 000000000..883c423b3
--- /dev/null
+++ b/tools/lsd/lsd.h
@@ -0,0 +1,10 @@
+#ifndef LSD_H
+#define LSD_H
+
+void lsd(char **execs, int num_execs,
+ int list_needed_libs,
+ int print_info,
+ char **lib_lookup_dirs,
+ int num_lib_lookup_dirs);
+
+#endif
diff --git a/tools/lsd/main.c b/tools/lsd/main.c
new file mode 100644
index 000000000..f29157a5b
--- /dev/null
+++ b/tools/lsd/main.c
@@ -0,0 +1,67 @@
+/* TODO:
+ 1. check the ARM EABI version--this works for versions 1 and 2.
+ 2. use a more-intelligent approach to finding the symbol table, symbol-string
+ table, and the .dynamic section.
+ 3. fix the determination of the host and ELF-file endianness
+ 4. write the help screen
+*/
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <elf.h>
+#include <gelf.h>
+#include <cmdline.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <lsd.h>
+
+/* Flag set by --verbose. This variable is global as it is accessed by the
+ macro INFO() in multiple compilation unites. */
+int verbose_flag = 0;
+/* Flag set by --quiet. This variable is global as it is accessed by the
+ macro PRINT() in multiple compilation unites. */
+int quiet_flag = 0;
+
+int main(int argc, char **argv)
+{
+ char **lookup_dirs = NULL;
+ int num_lookup_dirs;
+ int print_info;
+ int list_needed_libs;
+
+ /* Do not issue INFO() statements before you call get_options() to set
+ the verbose flag as necessary.
+ */
+
+ int first = get_options(argc, argv,
+ &list_needed_libs,
+ &print_info,
+ &lookup_dirs,
+ &num_lookup_dirs,
+ &verbose_flag);
+
+ if (first == argc) {
+ print_help();
+ FAILIF(1, "You must specify at least one input ELF file!\n");
+ }
+
+ /* Check to see whether the ELF library is current. */
+ FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
+
+ /* List symbol dependencies... */
+ lsd(&argv[first], argc - first,
+ list_needed_libs, print_info,
+ lookup_dirs, num_lookup_dirs);
+
+ FREE(lookup_dirs);
+
+ return 0;
+}
+
diff --git a/tools/mktarball.sh b/tools/mktarball.sh
new file mode 100755
index 000000000..ea1a8ed71
--- /dev/null
+++ b/tools/mktarball.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+
+# $1: path to fs_get_stats program
+# $2: start dir
+# $3: subdir to tar up (from $2)
+# $4: target tar name
+# $5: target tarball name (usually $(3).bz2)
+
+if [ $# -ne 5 ]; then
+ echo "Error: wrong number of arguments in cmd: $0 $* "
+ exit 1
+fi
+
+fs_get_stats=`readlink -f $1`
+start_dir=`readlink -f $2`
+dir_to_tar=$3
+target_tar=`readlink -f $4`
+target_tarball=`readlink -f $5`
+
+cd $2
+
+#tar --no-recursion -cvf ${target_tar} ${dir_to_tar}
+rm ${target_tar} > /dev/null 2>&1
+
+# do dirs first
+subdirs=`find ${dir_to_tar} -type d -print`
+files=`find ${dir_to_tar} \! -type d -print`
+for f in ${subdirs} ${files} ; do
+ curr_perms=`stat -c 0%a $f`
+ [ -d "$f" ] && is_dir=1 || is_dir=0
+ new_info=`${fs_get_stats} ${curr_perms} ${is_dir} ${f}`
+ new_uid=`echo ${new_info} | awk '{print $1;}'`
+ new_gid=`echo ${new_info} | awk '{print $2;}'`
+ new_perms=`echo ${new_info} | awk '{print $3;}'`
+# echo "$f: dir: $is_dir curr: $curr_perms uid: $new_uid gid: $new_gid "\
+# "perms: $new_perms"
+ tar --no-recursion --numeric-owner --owner $new_uid \
+ --group $new_gid --mode $new_perms -p -rf ${target_tar} ${f}
+done
+
+if [ $? -eq 0 ] ; then
+ bzip2 -c ${target_tar} > ${target_tarball}
+ success=$?
+ [ $success -eq 0 ] || rm -f ${target_tarball}
+ rm -f ${target_tar}
+ exit $success
+fi
+
+rm -f ${target_tar}
+exit 1
diff --git a/tools/print_module_licenses.sh b/tools/print_module_licenses.sh
new file mode 100755
index 000000000..b84f7d4c5
--- /dev/null
+++ b/tools/print_module_licenses.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+find . -name MODULE_LICENSE_\* | sed 's/\/MODULE_LICENSE_/\ /' | sed 's/\.\///' | awk '{ print $2 " " $1; }' | sort
diff --git a/tools/rgb2565/Android.mk b/tools/rgb2565/Android.mk
new file mode 100644
index 000000000..189584dbb
--- /dev/null
+++ b/tools/rgb2565/Android.mk
@@ -0,0 +1,17 @@
+# Copyright 2008 The Android Open Source Project
+#
+# Android.mk for rgb2565
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+# rgb2565 host tool
+# =========================================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := to565.c
+
+LOCAL_CFLAGS += -O2 -Wall -Wno-unused-parameter
+LOCAL_MODULE := rgb2565
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/tools/rgb2565/to565.c b/tools/rgb2565/to565.c
new file mode 100644
index 000000000..abf9cdb26
--- /dev/null
+++ b/tools/rgb2565/to565.c
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#define to565(r,g,b) \
+ ((((r) >> 3) << 11) | (((g) >> 2) << 5) | ((b) >> 3))
+
+#define from565_r(x) ((((x) >> 11) & 0x1f) * 255 / 31)
+#define from565_g(x) ((((x) >> 5) & 0x3f) * 255 / 63)
+#define from565_b(x) (((x) & 0x1f) * 255 / 31)
+
+void to_565_raw(void)
+{
+ unsigned char in[3];
+ unsigned short out;
+
+ while(read(0, in, 3) == 3) {
+ out = to565(in[0],in[1],in[2]);
+ write(1, &out, 2);
+ }
+ return;
+}
+
+void to_565_raw_dither(int width)
+{
+ unsigned char in[3];
+ unsigned short out;
+ int i = 0;
+ int e;
+
+ int* error = malloc((width+2) * 3 * sizeof(int));
+ int* next_error = malloc((width+2) * 3 * sizeof(int));
+ memset(error, 0, (width+2) * 3 * sizeof(int));
+ memset(next_error, 0, (width+2) * 3 * sizeof(int));
+ error += 3; // array goes from [-3..((width+1)*3+2)]
+ next_error += 3;
+
+ while(read(0, in, 3) == 3) {
+ int r = in[0] + error[i*3+0];
+ int rb = (r < 0) ? 0 : ((r > 255) ? 255 : r);
+
+ int g = in[1] + error[i*3+1];
+ int gb = (g < 0) ? 0 : ((g > 255) ? 255 : g);
+
+ int b = in[2] + error[i*3+2];
+ int bb = (b < 0) ? 0 : ((b > 255) ? 255 : b);
+
+ out = to565(rb, gb, bb);
+ write(1, &out, 2);
+
+#define apply_error(ch) { \
+ next_error[(i-1)*3+ch] += e * 3 / 16; \
+ next_error[(i)*3+ch] += e * 5 / 16; \
+ next_error[(i+1)*3+ch] += e * 1 / 16; \
+ error[(i+1)*3+ch] += e - ((e*1/16) + (e*3/16) + (e*5/16)); \
+ }
+
+ e = r - from565_r(out);
+ apply_error(0);
+
+ e = g - from565_g(out);
+ apply_error(1);
+
+ e = b - from565_b(out);
+ apply_error(2);
+
+#undef apply_error
+
+ ++i;
+ if (i == width) {
+ // error <- next_error; next_error <- 0
+ int* temp = error; error = next_error; next_error = temp;
+ memset(next_error, 0, (width+1) * 3 * sizeof(int));
+ i = 0;
+ }
+ }
+
+ free(error-3);
+ free(next_error-3);
+
+ return;
+}
+
+void to_565_rle(void)
+{
+ unsigned char in[3];
+ unsigned short last, color, count;
+ unsigned total = 0;
+ count = 0;
+
+ while(read(0, in, 3) == 3) {
+ color = to565(in[0],in[1],in[2]);
+ if (count) {
+ if ((color == last) && (count != 65535)) {
+ count++;
+ continue;
+ } else {
+ write(1, &count, 2);
+ write(1, &last, 2);
+ total += count;
+ }
+ }
+ last = color;
+ count = 1;
+ }
+ if (count) {
+ write(1, &count, 2);
+ write(1, &last, 2);
+ total += count;
+ }
+ fprintf(stderr,"%d pixels\n",total);
+}
+
+int main(int argc, char **argv)
+{
+ if ((argc == 2) && (!strcmp(argv[1],"-rle"))) {
+ to_565_rle();
+ } else {
+ if (argc > 2 && (!strcmp(argv[1], "-w"))) {
+ to_565_raw_dither(atoi(argv[2]));
+ } else {
+ to_565_raw();
+ }
+ }
+ return 0;
+}
diff --git a/tools/signapk/Android.mk b/tools/signapk/Android.mk
new file mode 100644
index 000000000..ccc76fdcf
--- /dev/null
+++ b/tools/signapk/Android.mk
@@ -0,0 +1,27 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+LOCAL_PATH := $(call my-dir)
+
+# the signapk tool (a .jar application used to sign packages)
+# ============================================================
+include $(CLEAR_VARS)
+LOCAL_MODULE := signapk
+LOCAL_SRC_FILES := SignApk.java
+LOCAL_JAR_MANIFEST := SignApk.mf
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# The post-build signing tools need signapk.jar.
+$(call dist-for-goals,droid,$(LOCAL_INSTALLED_MODULE))
diff --git a/tools/signapk/SignApk.java b/tools/signapk/SignApk.java
new file mode 100644
index 000000000..340a9f551
--- /dev/null
+++ b/tools/signapk/SignApk.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.signapk;
+
+import sun.misc.BASE64Encoder;
+import sun.security.pkcs.ContentInfo;
+import sun.security.pkcs.PKCS7;
+import sun.security.pkcs.SignerInfo;
+import sun.security.x509.AlgorithmId;
+import sun.security.x509.X500Name;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.security.AlgorithmParameters;
+import java.security.DigestOutputStream;
+import java.security.GeneralSecurityException;
+import java.security.Key;
+import java.security.KeyFactory;
+import java.security.MessageDigest;
+import java.security.PrivateKey;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.jar.Attributes;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import javax.crypto.Cipher;
+import javax.crypto.EncryptedPrivateKeyInfo;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.PBEKeySpec;
+
+/**
+ * Command line tool to sign JAR files (including APKs and OTA updates) in
+ * a way compatible with the mincrypt verifier, using SHA1 and RSA keys.
+ */
+class SignApk {
+ private static final String CERT_SF_NAME = "META-INF/CERT.SF";
+ private static final String CERT_RSA_NAME = "META-INF/CERT.RSA";
+
+ private static X509Certificate readPublicKey(File file)
+ throws IOException, GeneralSecurityException {
+ FileInputStream input = new FileInputStream(file);
+ try {
+ CertificateFactory cf = CertificateFactory.getInstance("X.509");
+ return (X509Certificate) cf.generateCertificate(input);
+ } finally {
+ input.close();
+ }
+ }
+
+ /**
+ * Reads the password from stdin and returns it as a string.
+ *
+ * @param keyFile The file containing the private key. Used to prompt the user.
+ */
+ private static String readPassword(File keyFile) {
+ // TODO: use Console.readPassword() when it's available.
+ System.out.print("Enter password for " + keyFile + " (password will not be hidden): ");
+ System.out.flush();
+ BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ try {
+ return stdin.readLine();
+ } catch (IOException ex) {
+ return null;
+ }
+ }
+
+ /**
+ * Decrypt an encrypted PKCS 8 format private key.
+ *
+ * Based on ghstark's post on Aug 6, 2006 at
+ * http://forums.sun.com/thread.jspa?threadID=758133&messageID=4330949
+ *
+ * @param encryptedPrivateKey The raw data of the private key
+ * @param keyFile The file containing the private key
+ */
+ private static KeySpec decryptPrivateKey(byte[] encryptedPrivateKey, File keyFile)
+ throws GeneralSecurityException {
+ EncryptedPrivateKeyInfo epkInfo;
+ try {
+ epkInfo = new EncryptedPrivateKeyInfo(encryptedPrivateKey);
+ } catch (IOException ex) {
+ // Probably not an encrypted key.
+ return null;
+ }
+
+ char[] password = readPassword(keyFile).toCharArray();
+
+ SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());
+ Key key = skFactory.generateSecret(new PBEKeySpec(password));
+
+ Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());
+ cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());
+
+ try {
+ return epkInfo.getKeySpec(cipher);
+ } catch (InvalidKeySpecException ex) {
+ System.err.println("signapk: Password for " + keyFile + " may be bad.");
+ throw ex;
+ }
+ }
+
+ /** Read a PKCS 8 format private key. */
+ private static PrivateKey readPrivateKey(File file)
+ throws IOException, GeneralSecurityException {
+ DataInputStream input = new DataInputStream(new FileInputStream(file));
+ try {
+ byte[] bytes = new byte[(int) file.length()];
+ input.read(bytes);
+
+ KeySpec spec = decryptPrivateKey(bytes, file);
+ if (spec == null) {
+ spec = new PKCS8EncodedKeySpec(bytes);
+ }
+
+ try {
+ return KeyFactory.getInstance("RSA").generatePrivate(spec);
+ } catch (InvalidKeySpecException ex) {
+ return KeyFactory.getInstance("DSA").generatePrivate(spec);
+ }
+ } finally {
+ input.close();
+ }
+ }
+
+ /** Add the SHA1 of every file to the manifest, creating it if necessary. */
+ private static Manifest addDigestsToManifest(JarFile jar)
+ throws IOException, GeneralSecurityException {
+ Manifest input = jar.getManifest();
+ Manifest output = new Manifest();
+ Attributes main = output.getMainAttributes();
+ if (input != null) {
+ main.putAll(input.getMainAttributes());
+ } else {
+ main.putValue("Manifest-Version", "1.0");
+ main.putValue("Created-By", "1.0 (Android SignApk)");
+ }
+
+ BASE64Encoder base64 = new BASE64Encoder();
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+ byte[] buffer = new byte[4096];
+ int num;
+
+ // We sort the input entries by name, and add them to the
+ // output manifest in sorted order. We expect that the output
+ // map will be deterministic.
+
+ TreeMap<String, JarEntry> byName = new TreeMap<String, JarEntry>();
+
+ for (Enumeration<JarEntry> e = jar.entries(); e.hasMoreElements(); ) {
+ JarEntry entry = e.nextElement();
+ byName.put(entry.getName(), entry);
+ }
+
+ for (JarEntry entry: byName.values()) {
+ String name = entry.getName();
+ if (!entry.isDirectory() && !name.equals(JarFile.MANIFEST_NAME) &&
+ !name.equals(CERT_SF_NAME) && !name.equals(CERT_RSA_NAME)) {
+ InputStream data = jar.getInputStream(entry);
+ while ((num = data.read(buffer)) > 0) {
+ md.update(buffer, 0, num);
+ }
+
+ Attributes attr = null;
+ if (input != null) attr = input.getAttributes(name);
+ attr = attr != null ? new Attributes(attr) : new Attributes();
+ attr.putValue("SHA1-Digest", base64.encode(md.digest()));
+ output.getEntries().put(name, attr);
+ }
+ }
+
+ return output;
+ }
+
+ /** Write to another stream and also feed it to the Signature object. */
+ private static class SignatureOutputStream extends FilterOutputStream {
+ private Signature mSignature;
+
+ public SignatureOutputStream(OutputStream out, Signature sig) {
+ super(out);
+ mSignature = sig;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ try {
+ mSignature.update((byte) b);
+ } catch (SignatureException e) {
+ throw new IOException("SignatureException: " + e);
+ }
+ super.write(b);
+ }
+
+ @Override
+ public void write(byte[] b, int off, int len) throws IOException {
+ try {
+ mSignature.update(b, off, len);
+ } catch (SignatureException e) {
+ throw new IOException("SignatureException: " + e);
+ }
+ super.write(b, off, len);
+ }
+ }
+
+ /** Write a .SF file with a digest the specified manifest. */
+ private static void writeSignatureFile(Manifest manifest, OutputStream out)
+ throws IOException, GeneralSecurityException {
+ Manifest sf = new Manifest();
+ Attributes main = sf.getMainAttributes();
+ main.putValue("Signature-Version", "1.0");
+ main.putValue("Created-By", "1.0 (Android SignApk)");
+
+ BASE64Encoder base64 = new BASE64Encoder();
+ MessageDigest md = MessageDigest.getInstance("SHA1");
+ PrintStream print = new PrintStream(
+ new DigestOutputStream(new ByteArrayOutputStream(), md),
+ true, "UTF-8");
+
+ // Digest of the entire manifest
+ manifest.write(print);
+ print.flush();
+ main.putValue("SHA1-Digest-Manifest", base64.encode(md.digest()));
+
+ Map<String, Attributes> entries = manifest.getEntries();
+ for (Map.Entry<String, Attributes> entry : entries.entrySet()) {
+ // Digest of the manifest stanza for this entry.
+ print.print("Name: " + entry.getKey() + "\r\n");
+ for (Map.Entry<Object, Object> att : entry.getValue().entrySet()) {
+ print.print(att.getKey() + ": " + att.getValue() + "\r\n");
+ }
+ print.print("\r\n");
+ print.flush();
+
+ Attributes sfAttr = new Attributes();
+ sfAttr.putValue("SHA1-Digest", base64.encode(md.digest()));
+ sf.getEntries().put(entry.getKey(), sfAttr);
+ }
+
+ sf.write(out);
+ }
+
+ /** Write a .RSA file with a digital signature. */
+ private static void writeSignatureBlock(
+ Signature signature, X509Certificate publicKey, OutputStream out)
+ throws IOException, GeneralSecurityException {
+ SignerInfo signerInfo = new SignerInfo(
+ new X500Name(publicKey.getIssuerX500Principal().getName()),
+ publicKey.getSerialNumber(),
+ AlgorithmId.get("SHA1"),
+ AlgorithmId.get("RSA"),
+ signature.sign());
+
+ PKCS7 pkcs7 = new PKCS7(
+ new AlgorithmId[] { AlgorithmId.get("SHA1") },
+ new ContentInfo(ContentInfo.DATA_OID, null),
+ new X509Certificate[] { publicKey },
+ new SignerInfo[] { signerInfo });
+
+ pkcs7.encodeSignedData(out);
+ }
+
+ /** Copy all the files in a manifest from input to output. */
+ private static void copyFiles(Manifest manifest,
+ JarFile in, JarOutputStream out) throws IOException {
+ byte[] buffer = new byte[4096];
+ int num;
+
+ Map<String, Attributes> entries = manifest.getEntries();
+ List<String> names = new ArrayList(entries.keySet());
+ Collections.sort(names);
+ for (String name : names) {
+ JarEntry inEntry = in.getJarEntry(name);
+ if (inEntry.getMethod() == JarEntry.STORED) {
+ // Preserve the STORED method of the input entry.
+ out.putNextEntry(new JarEntry(inEntry));
+ } else {
+ // Create a new entry so that the compressed len is recomputed.
+ JarEntry je = new JarEntry(name);
+ je.setTime(inEntry.getTime());
+ out.putNextEntry(je);
+ }
+
+ InputStream data = in.getInputStream(inEntry);
+ while ((num = data.read(buffer)) > 0) {
+ out.write(buffer, 0, num);
+ }
+ out.flush();
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 4) {
+ System.err.println("Usage: signapk " +
+ "publickey.x509[.pem] privatekey.pk8 " +
+ "input.jar output.jar");
+ System.exit(2);
+ }
+
+ JarFile inputJar = null;
+ JarOutputStream outputJar = null;
+
+ try {
+ X509Certificate publicKey = readPublicKey(new File(args[0]));
+
+ // Assume the certificate is valid for at least an hour.
+ long timestamp = publicKey.getNotBefore().getTime() + 3600L * 1000;
+
+ PrivateKey privateKey = readPrivateKey(new File(args[1]));
+ inputJar = new JarFile(new File(args[2]), false); // Don't verify.
+ outputJar = new JarOutputStream(new FileOutputStream(args[3]));
+ outputJar.setLevel(9);
+
+ JarEntry je;
+
+ // MANIFEST.MF
+ Manifest manifest = addDigestsToManifest(inputJar);
+ je = new JarEntry(JarFile.MANIFEST_NAME);
+ je.setTime(timestamp);
+ outputJar.putNextEntry(je);
+ manifest.write(outputJar);
+
+ // CERT.SF
+ Signature signature = Signature.getInstance("SHA1withRSA");
+ signature.initSign(privateKey);
+ je = new JarEntry(CERT_SF_NAME);
+ je.setTime(timestamp);
+ outputJar.putNextEntry(je);
+ writeSignatureFile(manifest,
+ new SignatureOutputStream(outputJar, signature));
+
+ // CERT.RSA
+ je = new JarEntry(CERT_RSA_NAME);
+ je.setTime(timestamp);
+ outputJar.putNextEntry(je);
+ writeSignatureBlock(signature, publicKey, outputJar);
+
+ // Everything else
+ copyFiles(manifest, inputJar, outputJar);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ } finally {
+ try {
+ if (inputJar != null) inputJar.close();
+ if (outputJar != null) outputJar.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+ }
+}
diff --git a/tools/signapk/SignApk.mf b/tools/signapk/SignApk.mf
new file mode 100644
index 000000000..2c72e591e
--- /dev/null
+++ b/tools/signapk/SignApk.mf
@@ -0,0 +1 @@
+Main-Class: com.android.signapk.SignApk
diff --git a/tools/signapk/test/run b/tools/signapk/test/run
new file mode 100755
index 000000000..4e2462557
--- /dev/null
+++ b/tools/signapk/test/run
@@ -0,0 +1,30 @@
+#!/usr/bin/make -f
+
+package := NotePad.apk
+
+all: out/signed-$(package)
+
+clean:
+ rm -rf out
+
+.PHONY: FORCE
+
+DSAPARAM := out/dsaparam
+$(DSAPARAM):
+ mkdir -p $(dir $@)
+ umask 0077 && openssl dsaparam -out $@ 1024
+
+%.pem: $(DSAPARAM) FORCE
+ mkdir -p $(dir $@)
+ umask 0077 && openssl gendsa -out $@.pk~ $(DSAPARAM)
+ umask 0077 && openssl pkcs8 -topk8 -nocrypt \
+ -in $@.pk~ -out $@.pk
+ umask 0077 && openssl req -new -x509 -key $@.pk -out $@ -days 1095 \
+ -subj "/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com"
+
+cert := out/key1.pem
+out/signed-$(package): $(package) $(cert)
+ mkdir -p $(dir $@)
+ SIGNAPK_DEBUG=1 \
+ signapk -input $< -output $@ \
+ -key $(cert).pk -cert $(cert) -tempdir out
diff --git a/tools/soslim/Android.mk b/tools/soslim/Android.mk
new file mode 100644
index 000000000..60a860a28
--- /dev/null
+++ b/tools/soslim/Android.mk
@@ -0,0 +1,49 @@
+# Copyright 2005 The Android Open Source Project
+#
+# Android.mk for soslim
+#
+
+LOCAL_PATH:= $(call my-dir)
+
+ifeq ($(TARGET_ARCH),arm)
+include $(CLEAR_VARS)
+
+LOCAL_LDLIBS += -ldl
+LOCAL_CFLAGS += -O2 -g
+LOCAL_CFLAGS += -fno-function-sections -fno-data-sections -fno-inline
+LOCAL_CFLAGS += -Wall -Wno-unused-function #-Werror
+LOCAL_CFLAGS += -DBIG_ENDIAN=1
+LOCAL_CFLAGS += -DARM_SPECIFIC_HACKS
+LOCAL_CFLAGS += -DSUPPORT_ANDROID_PRELINK_TAGS
+LOCAL_CFLAGS += -DDEBUG
+LOCAL_CFLAGS += -DSTRIP_STATIC_SYMBOLS
+LOCAL_CFLAGS += -DMOVE_SECTIONS_IN_RANGES
+
+ifeq ($(HOST_OS),windows)
+# Cygwin stat does not support ACCESSPERMS bitmask
+LOCAL_CFLAGS += -DACCESSPERMS=0777
+LOCAL_LDLIBS += -lintl
+endif
+
+LOCAL_SRC_FILES := \
+ cmdline.c \
+ common.c \
+ debug.c \
+ soslim.c \
+ main.c \
+ prelink_info.c \
+ symfilter.c
+
+LOCAL_C_INCLUDES:= \
+ $(LOCAL_PATH)/ \
+ external/elfutils/lib/ \
+ external/elfutils/libelf/ \
+ external/elfutils/libebl/ \
+ external/elfcopy/
+
+LOCAL_STATIC_LIBRARIES := libelfcopy libelf libebl libebl_arm #dl
+
+LOCAL_MODULE := soslim
+
+include $(BUILD_HOST_EXECUTABLE)
+endif #TARGET_ARCH==arm
diff --git a/tools/soslim/cmdline.c b/tools/soslim/cmdline.c
new file mode 100644
index 000000000..c2d5e7176
--- /dev/null
+++ b/tools/soslim/cmdline.c
@@ -0,0 +1,141 @@
+#include <debug.h>
+#include <cmdline.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <ctype.h>
+
+extern char *optarg;
+extern int optind, opterr, optopt;
+
+static struct option long_options[] =
+{
+ {"verbose", no_argument, 0, 'V'},
+ {"quiet", no_argument, 0, 'Q'},
+ {"shady", no_argument, 0, 'S'},
+ {"print", no_argument, 0, 'p'},
+ {"help", no_argument, 0, 'h'},
+ {"outfile", required_argument, 0, 'o'},
+ {"filter", required_argument, 0, 'f'},
+ {"dry", no_argument, 0, 'n'},
+ {"strip", no_argument, 0, 's'},
+ {0, 0, 0, 0},
+};
+
+/* This array must parallel long_options[] */
+static
+const char *descriptions[sizeof(long_options)/sizeof(long_options[0])] = {
+ "print verbose output",
+ "suppress errors and warnings",
+ "patch ABS symbols whose values coincide with section starts and ends",
+ "print the symbol table (if specified, only -V is allowed)",
+ "this help screen",
+ "specify an output file (if not provided, input file is modified)",
+ "specify a symbol-filter file",
+ "dry run (perform all calculations but do not modify the ELF file)",
+ "strip debug sections, if they are present"
+};
+
+void print_help(void)
+{
+ fprintf(stdout,
+ "invokation:\n"
+ "\tsoslim file1 [file2 file3 ... fileN] [-Ldir1 -Ldir2 ... -LdirN] "
+ "[-Vpn]\n"
+ "or\n"
+ "\tsoslim -h\n\n");
+ fprintf(stdout, "options:\n");
+ struct option *opt = long_options;
+ const char **desc = descriptions;
+ while (opt->name) {
+ fprintf(stdout, "\t-%c/--%-15s %s\n",
+ opt->val,
+ opt->name,
+ *desc);
+ opt++;
+ desc++;
+ }
+}
+
+int get_options(int argc, char **argv,
+ char **outfile,
+ char **symsfile,
+ int *print_symtab,
+ int *verbose,
+ int *quiet,
+ int *shady,
+ int *dry_run,
+ int *strip_debug)
+{
+ int c;
+
+ ASSERT(outfile);
+ *outfile = NULL;
+ ASSERT(symsfile);
+ *symsfile = NULL;
+ ASSERT(print_symtab);
+ *print_symtab = 0;
+ ASSERT(verbose);
+ *verbose = 0;
+ ASSERT(quiet);
+ *quiet = 0;
+ ASSERT(shady);
+ *shady = 0;
+ ASSERT(dry_run);
+ *dry_run = 0;
+ ASSERT(strip_debug);
+ *strip_debug = 0;
+
+ while (1) {
+ /* getopt_long stores the option index here. */
+ int option_index = 0;
+
+ c = getopt_long (argc, argv,
+ "QVSphi:o:y:Y:f:ns",
+ long_options,
+ &option_index);
+ /* Detect the end of the options. */
+ if (c == -1) break;
+
+ if (isgraph(c)) {
+ INFO ("option -%c with value `%s'\n", c, (optarg ?: "(null)"));
+ }
+
+#define SET_STRING_OPTION(name) do { \
+ ASSERT(optarg); \
+ *name = strdup(optarg); \
+} while(0)
+
+ switch (c) {
+ case 0:
+ /* If this option set a flag, do nothing else now. */
+ if (long_options[option_index].flag != 0)
+ break;
+ INFO ("option %s", long_options[option_index].name);
+ if (optarg)
+ INFO (" with arg %s", optarg);
+ INFO ("\n");
+ break;
+ case 'p': *print_symtab = 1; break;
+ case 'h': print_help(); exit(1); break;
+ case 'V': *verbose = 1; break;
+ case 'Q': *quiet = 1; break;
+ case 'S': *shady = 1; break;
+ case 'n': *dry_run = 1; break;
+ case 's': *strip_debug = 1; break;
+ case 'o': SET_STRING_OPTION(outfile); break;
+ case 'f': SET_STRING_OPTION(symsfile); break;
+ case '?':
+ /* getopt_long already printed an error message. */
+ break;
+
+#undef SET_STRING_OPTION
+
+ default:
+ FAILIF(1, "Unknown option");
+ }
+ }
+
+ return optind;
+}
diff --git a/tools/soslim/cmdline.h b/tools/soslim/cmdline.h
new file mode 100644
index 000000000..bfc431ee2
--- /dev/null
+++ b/tools/soslim/cmdline.h
@@ -0,0 +1,16 @@
+#ifndef CMDLINE_H
+#define CMDLINE_H
+
+void print_help(void);
+
+int get_options(int argc, char **argv,
+ char **outfile,
+ char **symsfile,
+ int *print_symtab,
+ int *verbose,
+ int *quiet,
+ int *shady,
+ int *dry_run,
+ int *strip_debug);
+
+#endif/*CMDLINE_H*/
diff --git a/tools/soslim/common.c b/tools/soslim/common.c
new file mode 100644
index 000000000..b90cf4199
--- /dev/null
+++ b/tools/soslim/common.c
@@ -0,0 +1,35 @@
+#include <stdlib.h>
+#include <common.h>
+#include <debug.h>
+
+void map_over_sections(Elf *elf,
+ section_match_fn_t match,
+ void *user_data)
+{
+ Elf_Scn* section = NULL;
+ while ((section = elf_nextscn(elf, section)) != NULL) {
+ if (match(elf, section, user_data))
+ return;
+ }
+}
+
+void map_over_segments(Elf *elf,
+ segment_match_fn_t match,
+ void *user_data)
+{
+ Elf32_Ehdr *ehdr;
+ Elf32_Phdr *phdr;
+ int index;
+
+ ehdr = elf32_getehdr(elf);
+ phdr = elf32_getphdr(elf);
+
+ INFO("Scanning over %d program segments...\n",
+ ehdr->e_phnum);
+
+ for (index = ehdr->e_phnum; index; index--) {
+ if (match(elf, phdr++, user_data))
+ return;
+ }
+}
+
diff --git a/tools/soslim/common.h b/tools/soslim/common.h
new file mode 100644
index 000000000..dacf930c6
--- /dev/null
+++ b/tools/soslim/common.h
@@ -0,0 +1,49 @@
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <libelf.h>
+#include <elf.h>
+
+#define unlikely(expr) __builtin_expect (expr, 0)
+#define likely(expr) __builtin_expect (expr, 1)
+
+#define MIN(a,b) ((a)<(b)?(a):(b)) /* no side effects in arguments allowed! */
+
+typedef int (*section_match_fn_t)(Elf *, Elf_Scn *, void *);
+void map_over_sections(Elf *, section_match_fn_t, void *);
+
+typedef int (*segment_match_fn_t)(Elf *, Elf32_Phdr *, void *);
+void map_over_segments(Elf *, segment_match_fn_t, void *);
+
+typedef struct {
+ Elf_Scn *sect;
+ Elf32_Shdr *hdr;
+ Elf_Data *data;
+ size_t index;
+} section_info_t;
+
+static inline void get_section_info(Elf_Scn *sect, section_info_t *info)
+{
+ info->sect = sect;
+ info->data = elf_getdata(sect, 0);
+ info->hdr = elf32_getshdr(sect);
+ info->index = elf_ndxscn(sect);
+}
+
+static inline int is_host_little(void)
+{
+ short val = 0x10;
+ return ((char *)&val)[0] != 0;
+}
+
+static inline long switch_endianness(long val)
+{
+ long newval;
+ ((char *)&newval)[3] = ((char *)&val)[0];
+ ((char *)&newval)[2] = ((char *)&val)[1];
+ ((char *)&newval)[1] = ((char *)&val)[2];
+ ((char *)&newval)[0] = ((char *)&val)[3];
+ return newval;
+}
+
+#endif/*COMMON_H*/
diff --git a/tools/soslim/debug.c b/tools/soslim/debug.c
new file mode 100644
index 000000000..b8365af4e
--- /dev/null
+++ b/tools/soslim/debug.c
@@ -0,0 +1,40 @@
+#include <debug.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#if 0
+
+#define NUM_COLS (32)
+
+int dump_hex_buffer(FILE *s, void *b, size_t len, size_t elsize) {
+ int num_nonprintable = 0;
+ int i, last;
+ char *pchr = (char *)b;
+ fputc('\n', s);
+ for (i = last = 0; i < len; i++) {
+ if (!elsize) {
+ if (i && !(i % 4)) fprintf(s, " ");
+ if (i && !(i % 8)) fprintf(s, " ");
+ } else {
+ if (i && !(i % elsize)) fprintf(s, " ");
+ }
+
+ if (i && !(i % NUM_COLS)) {
+ while (last < i) {
+ if (isprint(pchr[last]))
+ fputc(pchr[last], s);
+ else {
+ fputc('.', s);
+ num_nonprintable++;
+ }
+ last++;
+ }
+ fprintf(s, " (%d)\n", i);
+ }
+ fprintf(s, "%02x", (unsigned char)pchr[i]);
+ }
+ if (i && (i % NUM_COLS)) fputs("\n", s);
+ return num_nonprintable;
+}
+
+#endif
diff --git a/tools/soslim/debug.h b/tools/soslim/debug.h
new file mode 100644
index 000000000..e7a2f9a00
--- /dev/null
+++ b/tools/soslim/debug.h
@@ -0,0 +1,88 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <common.h>
+
+#ifdef DEBUG
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, "%s(%d): ", __FILE__, __LINE__); \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* Debug enabled */
+ #define ASSERT(x) do { \
+ if (unlikely(!(x))) { \
+ fprintf(stderr, \
+ "ASSERTION FAILURE %s:%d: [%s]\n", \
+ __FILE__, __LINE__, #x); \
+ exit(1); \
+ } \
+} while(0)
+
+#else
+
+ #define FAILIF(cond, msg...) do { \
+ if (unlikely(cond)) { \
+ fprintf(stderr, ##msg); \
+ exit(1); \
+ } \
+} while(0)
+
+/* No debug */
+ #define ASSERT(x) do { } while(0)
+
+#endif/* DEBUG */
+
+#define FAILIF_LIBELF(cond, function) \
+ FAILIF(cond, "%s(): %s\n", #function, elf_errmsg(elf_errno()));
+
+static inline void *MALLOC(unsigned int size) {
+ void *m = malloc(size);
+ FAILIF(NULL == m, "malloc(%d) failed!\n", size);
+ return m;
+}
+
+static inline void *CALLOC(unsigned int num_entries, unsigned int entry_size) {
+ void *m = calloc(num_entries, entry_size);
+ FAILIF(NULL == m, "calloc(%d, %d) failed!\n", num_entries, entry_size);
+ return m;
+}
+
+static inline void *REALLOC(void *ptr, unsigned int size) {
+ void *m = realloc(ptr, size);
+ FAILIF(NULL == m, "realloc(%p, %d) failed!\n", ptr, size);
+ return m;
+}
+
+static inline void FREE(void *ptr) {
+ free(ptr);
+}
+
+static inline void FREEIF(void *ptr) {
+ if (ptr) FREE(ptr);
+}
+
+#define PRINT(x...) do { \
+ extern int quiet_flag; \
+ if(likely(!quiet_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+#define ERROR(x...) fprintf(stderr, ##x)
+
+#define INFO(x...) do { \
+ extern int verbose_flag; \
+ if(unlikely(verbose_flag)) \
+ fprintf(stdout, ##x); \
+} while(0)
+
+/* Prints a hex and ASCII dump of the selected buffer to the selected stream. */
+int dump_hex_buffer(FILE *s, void *b, size_t l, size_t elsize);
+
+#endif/*DEBUG_H*/
diff --git a/tools/soslim/main.c b/tools/soslim/main.c
new file mode 100644
index 000000000..fa5a3158a
--- /dev/null
+++ b/tools/soslim/main.c
@@ -0,0 +1,360 @@
+/* TODO:
+ 1. check the ARM EABI version--this works for versions 1 and 2.
+ 2. use a more-intelligent approach to finding the symbol table, symbol-string
+ table, and the .dynamic section.
+ 3. fix the determination of the host and ELF-file endianness
+ 4. write the help screen
+*/
+
+#include <stdio.h>
+#include <common.h>
+#include <debug.h>
+#include <hash.h>
+#include <libelf.h>
+#include <elf.h>
+#include <gelf.h>
+#include <cmdline.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <soslim.h>
+#include <symfilter.h>
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+#include <prelink_info.h>
+#endif
+
+/* Flag set by --verbose. This variable is global as it is accessed by the
+ macro INFO() in multiple compilation unites. */
+int verbose_flag = 0;
+/* Flag set by --quiet. This variable is global as it is accessed by the
+ macro PRINT() in multiple compilation unites. */
+int quiet_flag = 0;
+static void print_dynamic_symbols(Elf *elf, const char *symtab_name);
+
+int main(int argc, char **argv)
+{
+ int elf_fd = -1, newelf_fd = -1;
+ Elf *elf = NULL, *newelf = NULL;
+ char *infile = NULL;
+ char *outfile = NULL;
+ char *symsfile_name = NULL;
+ int print_symtab = 0;
+ int shady = 0;
+ int dry_run = 0;
+ int strip_debug = 0;
+
+ /* Do not issue INFO() statements before you call get_options() to set
+ the verbose flag as necessary.
+ */
+
+ int first = get_options(argc, argv,
+ &outfile,
+ &symsfile_name,
+ &print_symtab,
+ &verbose_flag,
+ &quiet_flag,
+ &shady,
+ &dry_run,
+ &strip_debug);
+
+ if ((print_symtab && (first == argc)) ||
+ (!print_symtab && first + 1 != argc)) {
+ print_help();
+ FAILIF(1, "You must specify an input ELF file!\n");
+ }
+ FAILIF(print_symtab && (outfile || symsfile_name || shady),
+ "You cannot provide --print and --outfile, --filter options, or "
+ "--shady simultaneously!\n");
+ FAILIF(dry_run && outfile,
+ "You cannot have a dry run and output a file at the same time.");
+
+ /* Check to see whether the ELF library is current. */
+ FAILIF (elf_version(EV_CURRENT) == EV_NONE, "libelf is out of date!\n");
+
+ if (print_symtab) {
+
+ while (first < argc) {
+ infile = argv[first++];
+
+ INFO("Opening %s...\n", infile);
+ elf_fd = open(infile, O_RDONLY);
+ FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
+ infile,
+ strerror(errno),
+ errno);
+ INFO("Calling elf_begin(%s)...\n", infile);
+ elf = elf_begin(elf_fd, ELF_C_READ, NULL);
+ FAILIF_LIBELF(elf == NULL, elf_begin);
+
+ /* libelf can recognize COFF and A.OUT formats, but we handle only
+ ELF. */
+ FAILIF(elf_kind(elf) != ELF_K_ELF,
+ "Input file %s is not in ELF format!\n",
+ infile);
+
+ /* Make sure this is a shared library or an executable. */
+ {
+ GElf_Ehdr elf_hdr;
+ INFO("Making sure %s is a shared library or an executable.\n",
+ infile);
+ FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
+ FAILIF(elf_hdr.e_type != ET_DYN &&
+ elf_hdr.e_type != ET_EXEC,
+ "%s must be a shared library or an executable "
+ "(elf type is %d).\n",
+ infile,
+ elf_hdr.e_type);
+ }
+
+ print_dynamic_symbols(elf, infile);
+
+ FAILIF_LIBELF(elf_end(elf), elf_end);
+ FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+ infile, strerror(errno), errno);
+ }
+ }
+ else {
+ int elf_fd = -1;
+ Elf *elf = NULL;
+ infile = argv[first];
+
+ INFO("Opening %s...\n", infile);
+ elf_fd = open(infile, ((outfile == NULL && dry_run == 0) ? O_RDWR : O_RDONLY));
+ FAILIF(elf_fd < 0, "open(%s): %s (%d)\n",
+ infile,
+ strerror(errno),
+ errno);
+ INFO("Calling elf_begin(%s)...\n", infile);
+ elf = elf_begin(elf_fd,
+ ((outfile == NULL && dry_run == 0) ? ELF_C_RDWR : ELF_C_READ),
+ NULL);
+ FAILIF_LIBELF(elf == NULL, elf_begin);
+
+ /* libelf can recognize COFF and A.OUT formats, but we handle only ELF. */
+ FAILIF(elf_kind(elf) != ELF_K_ELF,
+ "Input file %s is not in ELF format!\n",
+ infile);
+
+ /* We run a better check in adjust_elf() itself. It is permissible to call adjust_elf()
+ on an executable if we are only stripping sections from the executable, not rearranging
+ or moving sections.
+ */
+ if (0) {
+ /* Make sure this is a shared library. */
+ GElf_Ehdr elf_hdr;
+ INFO("Making sure %s is a shared library...\n", infile);
+ FAILIF_LIBELF(0 == gelf_getehdr(elf, &elf_hdr), gelf_getehdr);
+ FAILIF(elf_hdr.e_type != ET_DYN,
+ "%s must be a shared library (elf type is %d, expecting %d).\n",
+ infile,
+ elf_hdr.e_type,
+ ET_DYN);
+ }
+
+ if (outfile != NULL) {
+ ASSERT(!dry_run);
+ struct stat st;
+ FAILIF(fstat (elf_fd, &st) != 0,
+ "Cannot stat input file %s: %s (%d)!\n",
+ infile, strerror(errno), errno);
+ newelf_fd = open (outfile, O_RDWR | O_CREAT | O_TRUNC,
+ st.st_mode & ACCESSPERMS);
+ FAILIF(newelf_fd < 0, "Cannot create file %s: %s (%d)!\n",
+ outfile, strerror(errno), errno);
+ INFO("Output file is [%s].\n", outfile);
+ newelf = elf_begin(newelf_fd, ELF_C_WRITE_MMAP, NULL);
+ } else {
+ INFO("Modifying [%s] in-place.\n", infile);
+ newelf = elf_clone(elf, ELF_C_EMPTY);
+ }
+
+ symfilter_t symfilter;
+
+ symfilter.symbols_to_keep = NULL;
+ symfilter.num_symbols_to_keep = 0;
+ if (symsfile_name) {
+ /* Make sure that the file is not empty. */
+ struct stat s;
+ FAILIF(stat(symsfile_name, &s) < 0,
+ "Cannot stat file %s.\n", symsfile_name);
+ if (s.st_size) {
+ INFO("Building symbol filter.\n");
+ build_symfilter(symsfile_name, elf, &symfilter, s.st_size);
+ }
+ else INFO("Not building symbol filter, filter file is empty.\n");
+ }
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ int prelinked = 0;
+ int elf_little; /* valid if prelinked != 0 */
+ long prelink_addr; /* valid if prelinked != 0 */
+#endif
+ clone_elf(elf, newelf,
+ infile, outfile,
+ symfilter.symbols_to_keep,
+ symfilter.num_symbols_to_keep,
+ shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ , &prelinked,
+ &elf_little,
+ &prelink_addr
+#endif
+ ,
+ true, /* rebuild the section-header-strings table */
+ strip_debug,
+ dry_run);
+
+ if (symsfile_name && symfilter.symbols_to_keep != NULL) {
+ destroy_symfilter(&symfilter);
+ }
+
+ if (outfile != NULL) INFO("Closing %s...\n", outfile);
+ FAILIF_LIBELF(elf_end (newelf) != 0, elf_end);
+ FAILIF(newelf_fd >= 0 && close(newelf_fd) < 0,
+ "Could not close file %s: %s (%d)!\n",
+ outfile, strerror(errno), errno);
+
+ INFO("Closing %s...\n", infile);
+ FAILIF_LIBELF(elf_end(elf), elf_end);
+ FAILIF(close(elf_fd) < 0, "Could not close file %s: %s (%d)!\n",
+ infile, strerror(errno), errno);
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ if (prelinked) {
+ INFO("File is prelinked, putting prelink TAG back in place.\n");
+ setup_prelink_info(outfile != NULL ? outfile : infile,
+ elf_little,
+ prelink_addr);
+ }
+#endif
+ }
+
+ FREEIF(outfile);
+ return 0;
+}
+
+static void print_dynamic_symbols(Elf *elf, const char *file)
+{
+ Elf_Scn *scn = NULL;
+ GElf_Shdr shdr;
+
+ GElf_Ehdr ehdr;
+ FAILIF_LIBELF(0 == gelf_getehdr(elf, &ehdr), gelf_getehdr);
+ while ((scn = elf_nextscn (elf, scn)) != NULL) {
+ FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr), gelf_getshdr);
+ if (SHT_DYNSYM == shdr.sh_type) {
+ /* This failure is too restrictive. There is no reason why
+ the symbol table couldn't be called something else, but
+ there is a standard name, and chances are that if we don't
+ see it, there's something wrong.
+ */
+ size_t shstrndx;
+ FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0,
+ elf_getshstrndx);
+ /* Now print the symbols. */
+ {
+ Elf_Data *symdata;
+ size_t elsize;
+ symdata = elf_getdata (scn, NULL); /* get the symbol data */
+ FAILIF_LIBELF(NULL == symdata, elf_getdata);
+ /* Get the number of section. We need to compare agains this
+ value for symbols that have special info in their section
+ references */
+ size_t shnum;
+ FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
+ /* Retrieve the size of a symbol entry */
+ elsize = gelf_fsize(elf, ELF_T_SYM, 1, ehdr.e_version);
+
+ size_t index;
+ for (index = 0; index < symdata->d_size / elsize; index++) {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+ /* Get the symbol. */
+ sym = gelf_getsymshndx (symdata, NULL,
+ index, &sym_mem, NULL);
+ FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
+ /* Print the symbol. */
+ char bind = '?';
+ switch(ELF32_ST_BIND(sym->st_info))
+ {
+ case STB_LOCAL: bind = 'l'; break;
+ case STB_GLOBAL: bind = 'g'; break;
+ case STB_WEAK: bind = 'w'; break;
+ default: break;
+ }
+ char type = '?';
+ switch(ELF32_ST_TYPE(sym->st_info))
+ {
+ case STT_NOTYPE: /* Symbol type is unspecified */
+ type = '?';
+ break;
+ case STT_OBJECT: /* Symbol is a data object */
+ type = 'o';
+ break;
+ case STT_FUNC: /* Symbol is a code object */
+ type = 'f';
+ break;
+ case STT_SECTION:/* Symbol associated with a section */
+ type = 's';
+ break;
+ case STT_FILE: /* Symbol's name is file name */
+ type = 'f';
+ break;
+ case STT_COMMON: /* Symbol is a common data object */
+ type = 'c';
+ break;
+ case STT_TLS: /* Symbol is thread-local data object*/
+ type = 't';
+ break;
+ }
+ {
+ int till_lineno;
+ int lineno;
+ const char *section_name = "(unknown)";
+ FAILIF(sym->st_shndx == SHN_XINDEX,
+ "Can't handle symbol's st_shndx == SHN_XINDEX!\n");
+ if (sym->st_shndx != SHN_UNDEF &&
+ sym->st_shndx < shnum) {
+ Elf_Scn *symscn = elf_getscn(elf, sym->st_shndx);
+ FAILIF_LIBELF(NULL == symscn, elf_getscn);
+ GElf_Shdr symscn_shdr;
+ FAILIF_LIBELF(NULL == gelf_getshdr(symscn,
+ &symscn_shdr),
+ gelf_getshdr);
+ section_name = elf_strptr(elf, shstrndx,
+ symscn_shdr.sh_name);
+ }
+ else if (sym->st_shndx == SHN_ABS) {
+ section_name = "SHN_ABS";
+ }
+ else if (sym->st_shndx == SHN_COMMON) {
+ section_name = "SHN_COMMON";
+ }
+ else if (sym->st_shndx == SHN_UNDEF) {
+ section_name = "(undefined)";
+ }
+ /* value size binding type section symname */
+ PRINT("%-15s %8d: %08llx %08llx %c%c %5d %n%s%n",
+ file,
+ index,
+ sym->st_value, sym->st_size, bind, type,
+ sym->st_shndx,
+ &till_lineno,
+ section_name,
+ &lineno);
+ lineno -= till_lineno;
+ /* Create padding for section names of 15 chars.
+ This limit is somewhat arbitratry. */
+ while (lineno++ < 15) PRINT(" ");
+ PRINT("(%d) %s\n",
+ sym->st_name,
+ elf_strptr(elf, shdr.sh_link, sym->st_name));
+ }
+ }
+ }
+ } /* if (shdr.sh_type = SHT_DYNSYM) */
+ } /* while ((scn = elf_nextscn (elf, scn)) != NULL) */
+}
diff --git a/tools/soslim/prelink_info.c b/tools/soslim/prelink_info.c
new file mode 100644
index 000000000..36516b121
--- /dev/null
+++ b/tools/soslim/prelink_info.c
@@ -0,0 +1,106 @@
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+
+#include <prelink_info.h>
+#include <debug.h>
+#include <common.h>
+
+typedef struct {
+ uint32_t mmap_addr;
+ char tag[4]; /* 'P', 'R', 'E', ' ' */
+} prelink_info_t __attribute__((packed));
+
+static inline void set_prelink(long *prelink_addr,
+ int elf_little,
+ prelink_info_t *info)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ if (prelink_addr) {
+ if (!(elf_little ^ is_host_little())) {
+ /* Same endianness */
+ *prelink_addr = info->mmap_addr;
+ }
+ else {
+ /* Different endianness */
+ *prelink_addr = switch_endianness(info->mmap_addr);
+ }
+ }
+}
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ int fd = open(fname, O_RDONLY);
+ FAILIF(fd < 0, "open(%s, O_RDONLY): %s (%d)!\n",
+ fname, strerror(errno), errno);
+ off_t end = lseek(fd, 0, SEEK_END);
+
+ int nr = sizeof(prelink_info_t);
+
+ off_t sz = lseek(fd, -nr, SEEK_CUR);
+ ASSERT((long)(end - sz) == (long)nr);
+ FAILIF(sz == (off_t)-1,
+ "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+ fd, strerror(errno), errno);
+
+ prelink_info_t info;
+ int num_read = read(fd, &info, nr);
+ FAILIF(num_read < 0,
+ "read(%d, &info, sizeof(prelink_info_t)): %s (%d)!\n",
+ fd, strerror(errno), errno);
+ FAILIF(num_read != sizeof(info),
+ "read(%d, &info, sizeof(prelink_info_t)): did not read %d bytes as "
+ "expected (read %d)!\n",
+ fd, sizeof(info), num_read);
+
+ int prelinked = 0;
+ if (!strncmp(info.tag, "PRE ", 4)) {
+ set_prelink(prelink_addr, elf_little, &info);
+ prelinked = 1;
+ }
+ FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+ return prelinked;
+}
+
+void setup_prelink_info(const char *fname, int elf_little, long base)
+{
+ FAILIF(sizeof(prelink_info_t) != 8, "Unexpected sizeof(prelink_info_t) == %d!\n", sizeof(prelink_info_t));
+ int fd = open(fname, O_WRONLY);
+ FAILIF(fd < 0,
+ "open(%s, O_WRONLY): %s (%d)\n" ,
+ fname, strerror(errno), errno);
+ prelink_info_t info;
+ off_t sz = lseek(fd, 0, SEEK_END);
+ FAILIF(sz == (off_t)-1,
+ "lseek(%d, 0, SEEK_END): %s (%d)!\n",
+ fd, strerror(errno), errno);
+
+ if (!(elf_little ^ is_host_little())) {
+ /* Same endianness */
+ INFO("Host and ELF file [%s] have same endianness.\n", fname);
+ info.mmap_addr = base;
+ }
+ else {
+ /* Different endianness */
+ INFO("Host and ELF file [%s] have different endianness.\n", fname);
+ info.mmap_addr = switch_endianness(base);
+ }
+ strncpy(info.tag, "PRE ", 4);
+
+ int num_written = write(fd, &info, sizeof(info));
+ FAILIF(num_written < 0,
+ "write(%d, &info, sizeof(info)): %s (%d)\n",
+ fd, strerror(errno), errno);
+ FAILIF(sizeof(info) != num_written,
+ "Could not write %d bytes (wrote only %d bytes) as expected!\n",
+ sizeof(info), num_written);
+ FAILIF(close(fd) < 0, "close(%d): %s (%d)!\n", fd, strerror(errno), errno);
+}
+
+#endif /*SUPPORT_ANDROID_PRELINK_TAGS*/
diff --git a/tools/soslim/prelink_info.h b/tools/soslim/prelink_info.h
new file mode 100644
index 000000000..e2787cb04
--- /dev/null
+++ b/tools/soslim/prelink_info.h
@@ -0,0 +1,9 @@
+#ifndef PRELINK_INFO_H
+#define PRELINK_INFO_H
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+
+int check_prelinked(const char *fname, int elf_little, long *prelink_addr);
+void setup_prelink_info(const char *fname, int elf_little, long base);
+
+#endif
+#endif/*PRELINK_INFO_H*/
diff --git a/tools/soslim/soslim.c b/tools/soslim/soslim.c
new file mode 100644
index 000000000..4e59c248c
--- /dev/null
+++ b/tools/soslim/soslim.c
@@ -0,0 +1,528 @@
+#include <stdio.h>
+//#include <common.h>
+#include <debug.h>
+#include <libelf.h>
+#include <libebl.h>
+#include <libebl_arm.h>
+#include <elf.h>
+#include <gelf.h>
+#include <string.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+#include <prelink_info.h>
+#endif
+
+#include <elfcopy.h>
+
+void clone_elf(Elf *elf, Elf *newelf,
+ const char *elf_name,
+ const char *newelf_name,
+ bool *sym_filter, int num_symbols,
+ int shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ , int *prelinked,
+ int *elf_little,
+ long *prelink_addr
+#endif
+ , bool rebuild_shstrtab,
+ bool strip_debug,
+ bool dry_run)
+{
+ GElf_Ehdr ehdr_mem, *ehdr; /* store ELF header of original library */
+ size_t shstrndx; /* section-strings-section index */
+ size_t shnum; /* number of sections in the original file */
+ /* string table for section headers in new file */
+ struct Ebl_Strtab *shst = NULL;
+ int dynamic_idx = -1; /* index in shdr_info[] of .dynamic section */
+ int dynsym_idx = -1; /* index in shdr_info[] of dynamic symbol table
+ section */
+
+ int cnt; /* general-purpose counter */
+ /* This flag is true when at least one section is dropped or when the
+ relative order of sections has changed, so that section indices in
+ the resulting file will be different from those in the original. */
+ bool sections_dropped_or_rearranged;
+ Elf_Scn *scn; /* general-purpose section */
+ size_t idx; /* general-purporse section index */
+
+ shdr_info_t *shdr_info = NULL;
+ int shdr_info_len = 0;
+ GElf_Phdr *phdr_info = NULL;
+
+ /* Get the information from the old file. */
+ ehdr = gelf_getehdr (elf, &ehdr_mem);
+ FAILIF_LIBELF(NULL == ehdr, gelf_getehdr);
+
+ /* Create new program header for the elf file */
+ FAILIF(gelf_newehdr (newelf, gelf_getclass (elf)) == 0 ||
+ (ehdr->e_type != ET_REL && gelf_newphdr (newelf,
+ ehdr->e_phnum) == 0),
+ "Cannot create new file: %s", elf_errmsg (-1));
+
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ ASSERT(prelinked);
+ ASSERT(prelink_addr);
+ ASSERT(elf_little);
+ *elf_little = (ehdr->e_ident[EI_DATA] == ELFDATA2LSB);
+ *prelinked = check_prelinked(elf_name, *elf_little, prelink_addr);
+#endif
+
+ INFO("\n\nCALCULATING MODIFICATIONS\n\n");
+
+ /* Copy out the old program header: notice that if the ELF file does not
+ have a program header, this loop won't execute.
+ */
+ INFO("Copying ELF program header...\n");
+ phdr_info = (GElf_Phdr *)CALLOC(ehdr->e_phnum, sizeof(GElf_Phdr));
+ for (cnt = 0; cnt < ehdr->e_phnum; ++cnt) {
+ INFO("\tRetrieving entry %d\n", cnt);
+ FAILIF_LIBELF(NULL == gelf_getphdr(elf, cnt, phdr_info + cnt),
+ gelf_getphdr);
+ /* -- we update the header at the end
+ FAILIF_LIBELF(gelf_update_phdr (newelf, cnt, phdr_info + cnt) == 0,
+ gelf_update_phdr);
+ */
+ }
+
+ /* Get the section-header strings section. This section contains the
+ strings used to name the other sections. */
+ FAILIF_LIBELF(elf_getshstrndx(elf, &shstrndx) < 0, elf_getshstrndx);
+
+ /* Get the number of sections. */
+ FAILIF_LIBELF(elf_getshnum (elf, &shnum) < 0, elf_getshnum);
+ INFO("Original ELF file has %d sections.\n", shnum);
+
+ /* Allocate the section-header-info buffer. We allocate one more entry
+ for the section-strings section because we regenerate that one and
+ place it at the very end of the file. Note that just because we create
+ an extra entry in the shdr_info array, it does not mean that we create
+ one more section the header. We just mark the old section for removal
+ and create one as the last section.
+ */
+ INFO("Allocating section-header info structure (%d) bytes...\n",
+ shnum*sizeof (shdr_info_t));
+ shdr_info_len = rebuild_shstrtab ? shnum + 1 : shnum;
+ shdr_info = (shdr_info_t *)CALLOC(shdr_info_len, sizeof (shdr_info_t));
+
+ /* Iterate over all the sections and initialize the internal section-info
+ array...
+ */
+ INFO("Initializing section-header info structure...\n");
+ /* Gather information about the sections in this file. */
+ scn = NULL;
+ cnt = 1;
+ while ((scn = elf_nextscn (elf, scn)) != NULL) {
+ ASSERT(elf_ndxscn(scn) == cnt);
+ shdr_info[cnt].scn = scn;
+ FAILIF_LIBELF(NULL == gelf_getshdr(scn, &shdr_info[cnt].shdr),
+ gelf_getshdr);
+
+ /* Get the name of the section. */
+ shdr_info[cnt].name = elf_strptr (elf, shstrndx,
+ shdr_info[cnt].shdr.sh_name);
+
+ INFO("\tname: %s\n", shdr_info[cnt].name);
+ FAILIF(shdr_info[cnt].name == NULL,
+ "Malformed file: section %d name is null\n",
+ cnt);
+
+ /* Mark them as present but not yet investigated. By "investigating"
+ sections, we mean that we check to see if by stripping other
+ sections, the sections under investigation will be compromised. For
+ example, if we are removing a section of code, then we want to make
+ sure that the symbol table does not contain symbols that refer to
+ this code, so we investigate the symbol table. If we do find such
+ symbols, we will not strip the code section.
+ */
+ shdr_info[cnt].idx = 1;
+
+ /* Remember the shdr.sh_link value. We need to remember this value
+ for those sections that refer to other sections. For example,
+ we need to remember it for relocation-entry sections, because if
+ we modify the symbol table that a relocation-entry section is
+ relative to, then we need to patch the relocation section. By the
+ time we get to deciding whether we need to patch the relocation
+ section, we will have overwritten its header's sh_link field with
+ a new value.
+ */
+ shdr_info[cnt].old_shdr = shdr_info[cnt].shdr;
+ INFO("\t\toriginal sh_link: %08d\n", shdr_info[cnt].old_shdr.sh_link);
+ INFO("\t\toriginal sh_addr: %lld\n", shdr_info[cnt].old_shdr.sh_addr);
+ INFO("\t\toriginal sh_offset: %lld\n",
+ shdr_info[cnt].old_shdr.sh_offset);
+ INFO("\t\toriginal sh_size: %lld\n", shdr_info[cnt].old_shdr.sh_size);
+
+ if (shdr_info[cnt].shdr.sh_type == SHT_DYNAMIC) {
+ INFO("\t\tthis is the SHT_DYNAMIC section [%s] at index %d\n",
+ shdr_info[cnt].name,
+ cnt);
+ dynamic_idx = cnt;
+ }
+ else if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM) {
+ INFO("\t\tthis is the SHT_DYNSYM section [%s] at index %d\n",
+ shdr_info[cnt].name,
+ cnt);
+ dynsym_idx = cnt;
+ }
+
+ FAILIF(shdr_info[cnt].shdr.sh_type == SHT_SYMTAB_SHNDX,
+ "Cannot handle sh_type SHT_SYMTAB_SHNDX!\n");
+ FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GROUP,
+ "Cannot handle sh_type SHT_GROUP!\n");
+ FAILIF(shdr_info[cnt].shdr.sh_type == SHT_GNU_versym,
+ "Cannot handle sh_type SHT_GNU_versym!\n");
+
+ /* Increment the counter. */
+ ++cnt;
+ } /* while */
+
+ /* Get the EBL handling. */
+ Ebl *ebl = ebl_openbackend (elf);
+ FAILIF_LIBELF(NULL == ebl, ebl_openbackend);
+ FAILIF_LIBELF(0 != arm_init(elf, ehdr->e_machine, ebl, sizeof(Ebl)),
+ arm_init);
+
+ if (strip_debug) {
+
+ /* This will actually strip more than just sections. It will strip
+ anything not essential to running the image.
+ */
+
+ INFO("Finding debug sections to strip.\n");
+
+ /* Now determine which sections can go away. The general rule is that
+ all sections which are not used at runtime are stripped out. But
+ there are a few exceptions:
+
+ - special sections named ".comment" and ".note" are kept
+ - OS or architecture specific sections are kept since we might not
+ know how to handle them
+ - if a section is referred to from a section which is not removed
+ in the sh_link or sh_info element it cannot be removed either
+ */
+ for (cnt = 1; cnt < shnum; ++cnt) {
+ /* Check whether the section can be removed. */
+ if (SECTION_STRIP_P (ebl, elf, ehdr, &shdr_info[cnt].shdr,
+ shdr_info[cnt].name,
+ 1, /* remove .comment sections */
+ 1 /* remove all debug sections */) ||
+ /* The macro above is broken--check for .comment explicitly */
+ !strcmp(".comment", shdr_info[cnt].name)
+#ifdef ARM_SPECIFIC_HACKS
+ ||
+ /* We ignore this section, that's why we can remove it. */
+ !strcmp(".stack", shdr_info[cnt].name)
+#endif
+ )
+ {
+ /* For now assume this section will be removed. */
+ INFO("Section [%s] will be stripped from image.\n",
+ shdr_info[cnt].name);
+ shdr_info[cnt].idx = 0;
+ }
+#ifdef STRIP_STATIC_SYMBOLS
+ else if (shdr_info[cnt].shdr.sh_type == SHT_SYMTAB) {
+ /* Mark the static symbol table for removal */
+ INFO("Section [%s] (static symbol table) will be stripped from image.\n",
+ shdr_info[cnt].name);
+ shdr_info[cnt].idx = 0;
+ if (shdr_info[shdr_info[cnt].shdr.sh_link].shdr.sh_type ==
+ SHT_STRTAB)
+ {
+ /* Mark the symbol table's string table for removal. */
+ INFO("Section [%s] (static symbol-string table) will be stripped from image.\n",
+ shdr_info[cnt].name);
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx = 0;
+ }
+ else {
+ ERROR("Expecting the sh_link field of a symbol table to point to"
+ " associated symbol-strings table! This is not mandated by"
+ " the standard, but is a common practice and the only way "
+ " to know for sure which strings table corresponds to which"
+ " symbol table!\n");
+ }
+ }
+#endif
+ }
+
+ /* Mark the SHT_NULL section as handled. */
+ shdr_info[0].idx = 2;
+
+ /* Handle exceptions: section groups and cross-references. We might have
+ to repeat this a few times since the resetting of the flag might
+ propagate.
+ */
+ int exceptions_pass = 0;
+ bool changes;
+ do {
+ changes = false;
+ INFO("\nHandling exceptions, pass %d\n\n", exceptions_pass++);
+ for (cnt = 1; cnt < shnum; ++cnt) {
+ if (shdr_info[cnt].idx == 0) {
+ /* If a relocation section is marked as being removed but the
+ section it is relocating is not, then do not remove the
+ relocation section.
+ */
+ if ((shdr_info[cnt].shdr.sh_type == SHT_REL
+ || shdr_info[cnt].shdr.sh_type == SHT_RELA)
+ && shdr_info[shdr_info[cnt].shdr.sh_info].idx != 0) {
+ PRINT("\tSection [%s] will not be removed because the "
+ "section it is relocating (%s) stays.\n",
+ shdr_info[cnt].name,
+ shdr_info[shdr_info[cnt].shdr.sh_info].name);
+ }
+ }
+ if (shdr_info[cnt].idx == 1) {
+ INFO("Processing section [%s]...\n", shdr_info[cnt].name);
+
+ /* The content of symbol tables we don't remove must not
+ reference any section which we do remove. Otherwise
+ we cannot remove the referred section.
+ */
+ if (shdr_info[cnt].shdr.sh_type == SHT_DYNSYM ||
+ shdr_info[cnt].shdr.sh_type == SHT_SYMTAB)
+ {
+ Elf_Data *symdata;
+ size_t elsize;
+
+ INFO("\tSection [%s] is a symbol table that's not being"
+ " removed.\n\tChecking to make sure that no symbols"
+ " refer to sections that are being removed.\n",
+ shdr_info[cnt].name);
+
+ /* Make sure the data is loaded. */
+ symdata = elf_getdata (shdr_info[cnt].scn, NULL);
+ FAILIF_LIBELF(NULL == symdata, elf_getdata);
+
+ /* Go through all symbols and make sure the section they
+ reference is not removed. */
+ elsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+
+ /* Check the length of the dynamic-symbol filter. */
+ FAILIF(sym_filter != NULL &&
+ num_symbols != symdata->d_size / elsize,
+ "Length of dynsym filter (%d) must equal the number"
+ " of dynamic symbols (%d)!\n",
+ num_symbols,
+ symdata->d_size / elsize);
+
+ size_t inner;
+ for (inner = 0;
+ inner < symdata->d_size / elsize;
+ ++inner)
+ {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+ size_t scnidx;
+
+ sym = gelf_getsymshndx (symdata, NULL,
+ inner, &sym_mem, NULL);
+ FAILIF_LIBELF(sym == NULL, gelf_getsymshndx);
+
+ scnidx = sym->st_shndx;
+ FAILIF(scnidx == SHN_XINDEX,
+ "Can't handle SHN_XINDEX!\n");
+ if (scnidx == SHN_UNDEF ||
+ scnidx >= shnum ||
+ (scnidx >= SHN_LORESERVE &&
+ scnidx <= SHN_HIRESERVE) ||
+ GELF_ST_TYPE (sym->st_info) == STT_SECTION)
+ {
+ continue;
+ }
+
+ /* If the symbol is going to be thrown and it is a
+ global or weak symbol that is defined (not imported),
+ then continue. Since the symbol is going away, we
+ do not care whether it refers to a section that is
+ also going away.
+ */
+ if (sym_filter && !sym_filter[inner])
+ {
+ bool global_or_weak =
+ ELF32_ST_BIND(sym->st_info) == STB_GLOBAL ||
+ ELF32_ST_BIND(sym->st_info) == STB_WEAK;
+ if (!global_or_weak && sym->st_shndx != SHN_UNDEF)
+ continue;
+ }
+
+ /* -- far too much output
+ INFO("\t\t\tSymbol [%s] (%d)\n",
+ elf_strptr(elf,
+ shdr_info[cnt].shdr.sh_link,
+ sym->st_name),
+ shdr_info[cnt].shdr.sh_info);
+ */
+
+ if (shdr_info[scnidx].idx == 0)
+ {
+ PRINT("\t\t\tSymbol [%s] refers to section [%s], "
+ "which is being removed. Will keep that "
+ "section.\n",
+ elf_strptr(elf,
+ shdr_info[cnt].shdr.sh_link,
+ sym->st_name),
+ shdr_info[scnidx].name);
+ /* Mark this section as used. */
+ shdr_info[scnidx].idx = 1;
+ changes |= scnidx < cnt;
+ }
+ } /* for each symbol */
+ } /* section type is SHT_DYNSYM or SHT_SYMTAB */
+ /* Cross referencing happens:
+ - for the cases the ELF specification says. That are
+ + SHT_DYNAMIC in sh_link to string table
+ + SHT_HASH in sh_link to symbol table
+ + SHT_REL and SHT_RELA in sh_link to symbol table
+ + SHT_SYMTAB and SHT_DYNSYM in sh_link to string table
+ + SHT_GROUP in sh_link to symbol table
+ + SHT_SYMTAB_SHNDX in sh_link to symbol table
+ Other (OS or architecture-specific) sections might as
+ well use this field so we process it unconditionally.
+ - references inside section groups
+ - specially marked references in sh_info if the SHF_INFO_LINK
+ flag is set
+ */
+
+ if (shdr_info[shdr_info[cnt].shdr.sh_link].idx == 0) {
+ shdr_info[shdr_info[cnt].shdr.sh_link].idx = 1;
+ changes |= shdr_info[cnt].shdr.sh_link < cnt;
+ }
+
+ /* Handle references through sh_info. */
+ if (SH_INFO_LINK_P (&shdr_info[cnt].shdr) &&
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx == 0) {
+ PRINT("\tSection [%s] links to section [%s], which was "
+ "marked for removal--it will not be removed.\n",
+ shdr_info[cnt].name,
+ shdr_info[shdr_info[cnt].shdr.sh_info].name);
+
+ shdr_info[shdr_info[cnt].shdr.sh_info].idx = 1;
+ changes |= shdr_info[cnt].shdr.sh_info < cnt;
+ }
+
+ /* Mark the section as investigated. */
+ shdr_info[cnt].idx = 2;
+ } /* if (shdr_info[cnt].idx == 1) */
+ } /* for (cnt = 1; cnt < shnum; ++cnt) */
+ } while (changes);
+ }
+ else {
+ INFO("Not stripping sections.\n");
+ /* Mark the SHT_NULL section as handled. */
+ shdr_info[0].idx = 2;
+ }
+
+ /* Mark the section header string table as unused, we will create
+ a new one as the very last section in the new ELF file.
+ */
+ shdr_info[shstrndx].idx = rebuild_shstrtab ? 0 : 2;
+
+ /* We need a string table for the section headers. */
+ FAILIF_LIBELF((shst = ebl_strtabinit (1 /* null-terminated */)) == NULL,
+ ebl_strtabinit);
+
+ /* Assign new section numbers. */
+ INFO("Creating new sections...\n");
+ //shdr_info[0].idx = 0;
+ for (cnt = idx = 1; cnt < shnum; ++cnt) {
+ if (shdr_info[cnt].idx > 0) {
+ shdr_info[cnt].idx = idx++;
+
+ /* Create a new section. */
+ FAILIF_LIBELF((shdr_info[cnt].newscn =
+ elf_newscn(newelf)) == NULL, elf_newscn);
+ ASSERT(elf_ndxscn (shdr_info[cnt].newscn) == shdr_info[cnt].idx);
+
+ /* Add this name to the section header string table. */
+ shdr_info[cnt].se = ebl_strtabadd (shst, shdr_info[cnt].name, 0);
+
+ INFO("\tsection [%s] (old offset %lld, old size %lld) will have index %d "
+ "(was %d).\n",
+ shdr_info[cnt].name,
+ shdr_info[cnt].old_shdr.sh_offset,
+ shdr_info[cnt].old_shdr.sh_size,
+ shdr_info[cnt].idx,
+ elf_ndxscn(shdr_info[cnt].scn));
+ } else {
+ INFO("\tIgnoring section [%s] (offset %lld, size %lld, index %d), "
+ "it will be discarded.\n",
+ shdr_info[cnt].name,
+ shdr_info[cnt].shdr.sh_offset,
+ shdr_info[cnt].shdr.sh_size,
+ elf_ndxscn(shdr_info[cnt].scn));
+ }
+ } /* for */
+
+ sections_dropped_or_rearranged = idx != cnt;
+
+ Elf_Data *shstrtab_data = NULL;
+
+#if 0
+ /* Fail if sections are being dropped or rearranged (except for moving shstrtab) or the
+ symbol filter is not empty, AND the file is an executable.
+ */
+ FAILIF(((idx != cnt && !(cnt - idx == 1 && rebuild_shstrtab)) || sym_filter != NULL) &&
+ ehdr->e_type != ET_DYN,
+ "You may not rearrange sections or strip symbols on an executable file!\n");
+#endif
+
+ INFO("\n\nADJUSTING ELF FILE\n\n");
+
+ adjust_elf(elf, elf_name,
+ newelf, newelf_name,
+ ebl,
+ ehdr, /* store ELF header of original library */
+ sym_filter, num_symbols,
+ shdr_info, shdr_info_len,
+ phdr_info,
+ idx, /* highest_scn_num */
+ shnum,
+ shstrndx,
+ shst,
+ sections_dropped_or_rearranged,
+ dynamic_idx, /* index in shdr_info[] of .dynamic section */
+ dynsym_idx, /* index in shdr_info[] of dynamic symbol table */
+ shady,
+ &shstrtab_data,
+ ehdr->e_type == ET_DYN, /* adjust section ofsets only when the file is a shared library */
+ rebuild_shstrtab);
+
+ /* We have everything from the old file. */
+ FAILIF_LIBELF(elf_cntl(elf, ELF_C_FDDONE) != 0, elf_cntl);
+
+ /* The ELF library better follows our layout when this is not a
+ relocatable object file. */
+ elf_flagelf (newelf,
+ ELF_C_SET,
+ (ehdr->e_type != ET_REL ? ELF_F_LAYOUT : 0));
+
+ /* Finally write the file. */
+ FAILIF_LIBELF(!dry_run && elf_update(newelf, ELF_C_WRITE) == -1, elf_update);
+
+ if (shdr_info != NULL) {
+ /* For some sections we might have created an table to map symbol
+ table indices. */
+ for (cnt = 1; cnt < shdr_info_len; ++cnt) {
+ FREEIF(shdr_info[cnt].newsymidx);
+ FREEIF(shdr_info[cnt].symse);
+ if(shdr_info[cnt].dynsymst != NULL)
+ ebl_strtabfree (shdr_info[cnt].dynsymst);
+ }
+ /* Free the memory. */
+ FREE (shdr_info);
+ }
+ FREEIF(phdr_info);
+
+ ebl_closebackend(ebl);
+
+ /* Free other resources. */
+ if (shst != NULL) ebl_strtabfree (shst);
+ if (shstrtab_data != NULL)
+ FREEIF(shstrtab_data->d_buf);
+}
diff --git a/tools/soslim/soslim.h b/tools/soslim/soslim.h
new file mode 100644
index 000000000..dfcb0856b
--- /dev/null
+++ b/tools/soslim/soslim.h
@@ -0,0 +1,32 @@
+#ifndef ELFCOPY_H
+#define ELFCOPY_H
+
+#include <libelf.h>
+#include <libebl.h>
+#include <elf.h>
+#include <gelf.h>
+
+/*
+symbol_filter:
+ On input: symbol_filter[i] indicates whether to keep a symbol (1) or to
+ remove it from the symbol table.
+ On output: symbol_filter[i] indicates whether a symbol was removed (0) or
+ kept (1) in the symbol table.
+*/
+
+void clone_elf(Elf *elf, Elf *newelf,
+ const char *elf_name,
+ const char *newelf_name,
+ bool *symbol_filter,
+ int num_symbols,
+ int shady
+#ifdef SUPPORT_ANDROID_PRELINK_TAGS
+ , int *prelinked,
+ int *elf_little,
+ long *prelink_addr
+#endif
+ , bool rebuild_shstrtab,
+ bool strip_debug,
+ bool dry_run);
+
+#endif/*ELFCOPY_H*/
diff --git a/tools/soslim/symfilter.c b/tools/soslim/symfilter.c
new file mode 100644
index 000000000..c21ab2ea5
--- /dev/null
+++ b/tools/soslim/symfilter.c
@@ -0,0 +1,242 @@
+#include <debug.h>
+#include <common.h>
+#include <symfilter.h>
+#include <hash.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <ctype.h>
+
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data);
+static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data);
+
+void build_symfilter(const char *name, Elf *elf, symfilter_t *filter,
+ off_t fsize)
+{
+ char *line = NULL;
+ symfilter_list_t *symbol;
+
+ FAILIF(NULL == name,
+ "You must provide a list of symbols to filter on!\n");
+
+ filter->num_symbols = 0;
+ filter->total_name_length = 0;
+
+ /* Open the file. */
+ INFO("Opening symbol-filter file %s...\n", name);
+ filter->fd = open(name, O_RDONLY);
+ FAILIF(filter->fd < 0, "open(%s): %s (%d)\n",
+ name,
+ strerror(errno),
+ errno);
+
+ INFO("Symbol-filter file %s is %ld bytes long...\n",
+ name,
+ fsize);
+ filter->fsize = fsize;
+
+ /* mmap the symbols file */
+ filter->mmap = mmap(NULL, fsize,
+ PROT_READ | PROT_WRITE, MAP_PRIVATE,
+ filter->fd, 0);
+ FAILIF(MAP_FAILED == filter->mmap,
+ "mmap(NULL, %ld, PROT_READ, MAP_PRIVATE, %d, 0): %s (%d)\n",
+ fsize,
+ filter->fd,
+ strerror(errno),
+ errno);
+ INFO("Memory-mapped symbol-filter file at %p\n", filter->mmap);
+
+ /* Make sure that the ELF file has a hash table. We will use the hash
+ table to look up symbols quickly. If the library does not have a hash-
+ table section, we can still do a linear scan, but the code for that is
+ not written, as practically every shared library has a hash table.
+ */
+
+ filter->symtab.sect = NULL;
+ map_over_sections(elf, match_dynsym_section, filter);
+ FAILIF(NULL == filter->symtab.sect,
+ "There is no dynamic-symbol table in this library.\n");
+ filter->hash.sect = NULL;
+ map_over_sections(elf, match_hash_table_section, filter);
+ FAILIF(NULL == filter->hash.sect,
+ "There is no hash table in this library.\n");
+ INFO("Hash table size 0x%lx, data size 0x%lx.\n",
+ (unsigned long)filter->hash.hdr->sh_size,
+ (unsigned long)filter->hash.data->d_size);
+
+ INFO("Hash table file offset: 0x%x\n", filter->hash.hdr->sh_offset);
+
+ GElf_Ehdr *ehdr, ehdr_mem;
+ ehdr = gelf_getehdr(elf, &ehdr_mem);
+ size_t symsize = gelf_fsize (elf, ELF_T_SYM, 1, ehdr->e_version);
+ ASSERT(symsize);
+ filter->num_symbols_to_keep = filter->symtab.data->d_size / symsize;
+ filter->symbols_to_keep = (bool *)CALLOC(filter->num_symbols_to_keep,
+ sizeof(bool));
+
+ /* Build the symbol-name chain. */
+ INFO("Building symbol list...\n");
+
+ line = (char *)filter->mmap;
+
+ filter->symbols = NULL;
+#define NOT_DONE ((off_t)(line - (char *)filter->mmap) < fsize)
+ do {
+ char *name = line;
+
+ /* Advance to the next line. We seek out spaces or new lines. At the
+ first space or newline character we find, we place a '\0', and
+ continue till we've consumed the line. For new lines, we scan both
+ '\r' and '\n'. For spaces, we look for ' ', '\t', and '\f'
+ */
+
+ while (NOT_DONE && !isspace(*line)) line++;
+ if (likely(NOT_DONE)) {
+ *line++ = '\0';
+ if (line - name > 1) {
+ /* Add the entry to the symbol-filter list */
+ symbol = (symfilter_list_t *)MALLOC(sizeof(symfilter_list_t));
+ symbol->next = filter->symbols;
+ symbol->name = name;
+ filter->symbols = symbol;
+
+#if 0
+ /* SLOW! For debugging only! */
+ {
+ size_t idx;
+ size_t elsize = gelf_fsize(elf, ELF_T_SYM, 1,
+ ehdr->e_version);
+ symbol->index = SHN_UNDEF;
+ for (idx = 0; idx < filter->symtab.data->d_size / elsize;
+ idx++) {
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+ const char *symname;
+ sym = gelf_getsymshndx (filter->symtab.data, NULL,
+ idx, &sym_mem, NULL);
+ ASSERT(sym);
+
+ symname = elf_strptr(elf,
+ filter->symtab.hdr->sh_link,
+ sym->st_name);
+ if(!strcmp(symname, symbol->name)) {
+ symbol->index = idx;
+ break;
+ }
+ }
+ }
+#else
+ /* Look up the symbol in the ELF file and associate it with the
+ entry in the filter. */
+ symbol->index = hash_lookup(elf,
+ &filter->hash,
+ &filter->symtab,
+ symbol->name,
+ &symbol->symbol);
+#endif
+ symbol->len = line - name - 1;
+ ASSERT(symbol->len == strlen(symbol->name));
+
+ /* If we didn't find the symbol, then it's not in the library.
+ */
+
+ if(STN_UNDEF == symbol->index) {
+ PRINT("%s: symbol was not found!\n", symbol->name);
+ }
+ else {
+ /* If we found the symbol but it's an undefined symbol, then
+ it's not in the library as well. */
+ GElf_Sym sym_mem;
+ GElf_Sym *sym;
+ sym = gelf_getsymshndx (filter->symtab.data, NULL,
+ symbol->index, &sym_mem, NULL);
+ FAILIF_LIBELF(NULL == sym, gelf_getsymshndx);
+ /* Make sure the hash lookup worked. */
+ ASSERT(!strcmp(elf_strptr(elf,
+ filter->symtab.hdr->sh_link,
+ sym->st_name),
+ symbol->name));
+ if (sym->st_shndx == SHN_UNDEF) {
+ PRINT("%s: symbol was not found (undefined)!\n", symbol->name);
+ }
+ else {
+ filter->num_symbols++;
+ /* Total count includes null terminators */
+ filter->total_name_length += symbol->len + 1;
+
+ /* Set the flag in the symbols_to_keep[] array. This indicates
+ to function copy_elf() that we want to keep the symbol.
+ */
+ filter->symbols_to_keep[symbol->index] = true;
+ INFO("FILTER-SYMBOL: [%s] [%d bytes]\n",
+ symbol->name,
+ symbol->len);
+ }
+ }
+ }
+ }
+ } while (NOT_DONE);
+#undef NOT_DONE
+}
+
+void destroy_symfilter(symfilter_t *filter)
+{
+ symfilter_list_t *old;
+ INFO("Destroying symbol list...\n");
+ while ((old = filter->symbols)) {
+ filter->symbols = old->next;
+ FREE(old);
+ }
+ munmap(filter->mmap, filter->fsize);
+ close(filter->fd);
+}
+
+static int match_hash_table_section(Elf *elf, Elf_Scn *sect, void *data)
+{
+ symfilter_t *filter = (symfilter_t *)data;
+ Elf32_Shdr *shdr;
+
+ ASSERT(filter);
+ ASSERT(sect);
+ shdr = elf32_getshdr(sect);
+
+ /* The section must be marked both as a SHT_HASH, and it's sh_link field
+ must contain the index of our symbol table (per ELF-file spec).
+ */
+ if (shdr->sh_type == SHT_HASH)
+ {
+ FAILIF(filter->hash.sect != NULL,
+ "There is more than one hash table!\n");
+ get_section_info(sect, &filter->hash);
+ }
+
+ return 0; /* keep looking */
+}
+
+static int match_dynsym_section(Elf *elf, Elf_Scn *sect, void *data)
+{
+ symfilter_t *filter = (symfilter_t *)data;
+ Elf32_Shdr *shdr;
+
+ ASSERT(filter);
+ ASSERT(sect);
+ shdr = elf32_getshdr(sect);
+
+ if (shdr->sh_type == SHT_DYNSYM)
+ {
+ FAILIF(filter->symtab.sect != NULL,
+ "There is more than one dynamic symbol table!\n");
+ get_section_info(sect, &filter->symtab);
+ }
+
+ return 0; /* keep looking */
+}
diff --git a/tools/soslim/symfilter.h b/tools/soslim/symfilter.h
new file mode 100644
index 000000000..f73fd506c
--- /dev/null
+++ b/tools/soslim/symfilter.h
@@ -0,0 +1,50 @@
+#ifndef SYMFILTER_H
+#define SYMFILTER_H
+
+/* This file describes the interface for parsing the list of symbols. Currently,
+ this is just a text file with each symbol on a separate line. We build an
+ in-memory linked list of symbols out of this image.
+*/
+
+#include <stdio.h>
+#include <libelf.h>
+#include <gelf.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <libebl.h> /* defines bool */
+
+typedef struct symfilter_list_t symfilter_list_t;
+struct symfilter_list_t {
+ symfilter_list_t *next;
+ const char *name;
+ unsigned int len; /* strlen(name) */
+ Elf32_Word index;
+ GElf_Sym symbol;
+};
+
+typedef struct symfilter_t {
+
+ int fd; /* symbol-filter-file descriptor */
+ off_t fsize; /* size of file */
+ void *mmap; /* symbol-fiter-file memory mapping */
+
+ section_info_t symtab;
+ section_info_t hash;
+ symfilter_list_t *symbols;
+
+ /* The total number of symbols in the symfilter. */
+ unsigned int num_symbols;
+ /* The total number of bytes occupied by the names of the symbols, including
+ the terminating null characters.
+ */
+ unsigned int total_name_length;
+
+ bool *symbols_to_keep;
+ /* must be the same as the number of symbols in the dynamic table! */
+ int num_symbols_to_keep;
+} symfilter_t;
+
+void build_symfilter(const char *name, Elf *elf, symfilter_t *filter, off_t);
+void destroy_symfilter(symfilter_t *);
+
+#endif/*SYMFILTER_H*/
diff --git a/tools/zipalign/Android.mk b/tools/zipalign/Android.mk
new file mode 100644
index 000000000..e23b699c6
--- /dev/null
+++ b/tools/zipalign/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright 2008 The Android Open Source Project
+#
+# Zip alignment tool
+#
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ ZipAlign.cpp
+
+LOCAL_C_INCLUDES += external/zlib
+
+LOCAL_STATIC_LIBRARIES := \
+ libutils \
+ libcutils
+
+LOCAL_LDLIBS := -lz
+
+ifeq ($(HOST_OS),linux)
+LOCAL_LDLIBS += -lrt
+endif
+
+# dunno if we need this, but some of the other tools include it
+ifeq ($(HOST_OS),windows)
+ifeq ($(strip $(USE_CYGWIN),),)
+LOCAL_LDLIBS += -lws2_32
+endif
+endif
+
+LOCAL_MODULE := zipalign
+
+include $(BUILD_HOST_EXECUTABLE)
+
diff --git a/tools/zipalign/README.txt b/tools/zipalign/README.txt
new file mode 100644
index 000000000..a2e1a5ef4
--- /dev/null
+++ b/tools/zipalign/README.txt
@@ -0,0 +1,31 @@
+zipalign -- zip archive alignment tool
+
+usage: zipalign [-f] [-v] <align> infile.zip outfile.zip
+
+ -f : overwrite existing outfile.zip
+ -v : verbose output
+ <align> is in bytes, e.g. "4" provides 32-bit alignment
+ infile.zip is an existing Zip archive
+ outfile.zip will be created
+
+
+The purpose of zipalign is to ensure that all uncompressed data starts
+with a particular alignment relative to the start of the file. This
+allows those portions to be accessed directly with mmap() even if they
+contain binary data with alignment restrictions.
+
+Some data needs to be word-aligned for easy access, others might benefit
+from being page-aligned. The adjustment is made by altering the size of
+the "extra" field in the zip Local File Header sections. Existing data
+in the "extra" fields may be altered by this process.
+
+Compressed data isn't very useful until it's uncompressed, so there's no
+need to adjust its alignment.
+
+Alterations to the archive, such as renaming or deleting entries, will
+potentially disrupt the alignment of the modified entry and all later
+entries. Files added to an "aligned" archive will not be aligned.
+
+By default, zipalign will not overwrite an existing output file. With the
+"-f" flag, an existing file will be overwritten.
+
diff --git a/tools/zipalign/ZipAlign.cpp b/tools/zipalign/ZipAlign.cpp
new file mode 100644
index 000000000..9e3cb66d6
--- /dev/null
+++ b/tools/zipalign/ZipAlign.cpp
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Zip alignment tool
+ */
+#include "utils/ZipFile.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+using namespace android;
+
+/*
+ * Show program usage.
+ */
+void usage(void)
+{
+ fprintf(stderr, "Zip alignment utility\n");
+ fprintf(stderr,
+ "Usage: zipalign [-f] [-v] <align> infile.zip outfile.zip\n");
+}
+
+/*
+ * Copy all entries from "pZin" to "pZout", aligning as needed.
+ */
+static int copyAndAlign(ZipFile* pZin, ZipFile* pZout, int alignment)
+{
+ int numEntries = pZin->getNumEntries();
+ ZipEntry* pEntry;
+ int bias = 0;
+ status_t status;
+
+ for (int i = 0; i < numEntries; i++) {
+ ZipEntry* pNewEntry;
+ int padding = 0;
+
+ pEntry = pZin->getEntryByIndex(i);
+ if (pEntry == NULL) {
+ fprintf(stderr, "ERROR: unable to retrieve entry %d\n", i);
+ return 1;
+ }
+
+ if (pEntry->isCompressed()) {
+ /* copy the entry without padding */
+ //printf("--- %s: orig at %ld len=%ld (compressed)\n",
+ // pEntry->getFileName(), (long) pEntry->getFileOffset(),
+ // (long) pEntry->getUncompressedLen());
+
+ } else {
+ /*
+ * Copy the entry, adjusting as required. We assume that the
+ * file position in the new file will be equal to the file
+ * position in the original.
+ */
+ long newOffset = pEntry->getFileOffset() + bias;
+ padding = (alignment - (newOffset % alignment)) % alignment;
+
+ //printf("--- %s: orig at %ld(+%d) len=%ld, adding pad=%d\n",
+ // pEntry->getFileName(), (long) pEntry->getFileOffset(),
+ // bias, (long) pEntry->getUncompressedLen(), padding);
+ }
+
+ status = pZout->add(pZin, pEntry, padding, &pNewEntry);
+ if (status != NO_ERROR)
+ return 1;
+ bias += padding;
+ //printf(" added '%s' at %ld (pad=%d)\n",
+ // pNewEntry->getFileName(), (long) pNewEntry->getFileOffset(),
+ // padding);
+ }
+
+ return 0;
+}
+
+/*
+ * Process a file. We open the input and output files, failing if the
+ * output file exists and "force" wasn't specified.
+ */
+static int process(const char* inFileName, const char* outFileName,
+ int alignment, bool force)
+{
+ ZipFile zin, zout;
+
+ //printf("PROCESS: align=%d in='%s' out='%s' force=%d\n",
+ // alignment, inFileName, outFileName, force);
+
+ /* this mode isn't supported -- do a trivial check */
+ if (strcmp(inFileName, outFileName) == 0) {
+ fprintf(stderr, "Input and output can't be same file\n");
+ return 1;
+ }
+
+ /* don't overwrite existing unless given permission */
+ if (!force && access(outFileName, F_OK) == 0) {
+ fprintf(stderr, "Output file '%s' exists\n", outFileName);
+ return 1;
+ }
+
+ if (zin.open(inFileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
+ fprintf(stderr, "Unable to open '%s' as zip archive\n", inFileName);
+ return 1;
+ }
+ if (zout.open(outFileName,
+ ZipFile::kOpenReadWrite|ZipFile::kOpenCreate|ZipFile::kOpenTruncate)
+ != NO_ERROR)
+ {
+ fprintf(stderr, "Unable to open '%s' as zip archive\n", inFileName);
+ return 1;
+ }
+
+ int result = copyAndAlign(&zin, &zout, alignment);
+ if (result != 0) {
+ printf("zipalign: failed rewriting '%s' to '%s'\n",
+ inFileName, outFileName);
+ }
+ return result;
+}
+
+/*
+ * Verify the alignment of a zip archive.
+ */
+static int verify(const char* fileName, int alignment, bool verbose)
+{
+ ZipFile zipFile;
+ bool foundBad = false;
+
+ if (verbose)
+ printf("Verifying alignment of %s (%d)...\n", fileName, alignment);
+
+ if (zipFile.open(fileName, ZipFile::kOpenReadOnly) != NO_ERROR) {
+ fprintf(stderr, "Unable to open '%s' for verification\n", fileName);
+ return 1;
+ }
+
+ int numEntries = zipFile.getNumEntries();
+ ZipEntry* pEntry;
+
+ for (int i = 0; i < numEntries; i++) {
+ pEntry = zipFile.getEntryByIndex(i);
+ if (pEntry->isCompressed()) {
+ if (verbose) {
+ printf("%8ld %s (OK - compressed)\n",
+ (long) pEntry->getFileOffset(), pEntry->getFileName());
+ }
+ } else {
+ long offset = pEntry->getFileOffset();
+ if ((offset % alignment) != 0) {
+ if (verbose) {
+ printf("%8ld %s (BAD - %ld)\n",
+ (long) offset, pEntry->getFileName(),
+ offset % alignment);
+ }
+ foundBad = true;
+ } else {
+ if (verbose) {
+ printf("%8ld %s (OK)\n",
+ (long) offset, pEntry->getFileName());
+ }
+ }
+ }
+ }
+
+ if (verbose)
+ printf("Verification %s\n", foundBad ? "FAILED" : "succesful");
+
+ return foundBad ? 1 : 0;
+}
+
+/*
+ * Parse args.
+ */
+int main(int argc, char* const argv[])
+{
+ bool wantUsage = false;
+ bool force = false;
+ bool verbose = false;
+ int result = 1;
+ int alignment;
+ char* endp;
+
+ if (argc < 4) {
+ wantUsage = true;
+ goto bail;
+ }
+
+ argc--;
+ argv++;
+
+ while (argc && argv[0][0] == '-') {
+ const char* cp = argv[0] +1;
+
+ while (*cp != '\0') {
+ switch (*cp) {
+ case 'f':
+ force = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ default:
+ fprintf(stderr, "ERROR: unknown flag -%c\n", *cp);
+ wantUsage = true;
+ goto bail;
+ }
+
+ cp++;
+ }
+
+ argc--;
+ argv++;
+ }
+
+ if (argc != 3) {
+ wantUsage = true;
+ goto bail;
+ }
+
+ alignment = strtol(argv[0], &endp, 10);
+ if (*endp != '\0' || alignment <= 0) {
+ fprintf(stderr, "Invalid value for alignment: %s\n", argv[0]);
+ wantUsage = true;
+ goto bail;
+ }
+
+ /* create the new archive */
+ result = process(argv[1], argv[2], alignment, force);
+
+ /* trust, but verify */
+ if (result == 0)
+ result = verify(argv[2], alignment, verbose);
+
+bail:
+ if (wantUsage) {
+ usage();
+ result = 2;
+ }
+
+ return result;
+}
+