In this document
Understanding the makefile
Layers
Building the Android Platform
Building the Android Kernel
Build Variants
makefile
Understanding the makefile
A makefile defines how to build a particular application. Makefiles typically include all of the following elements:
Name: Give your build a name (LOCAL_MODULE := ).
Local Variables: Clear local variables with CLEAR_VARS (include $(CLEAR_VARS)).
Files: Determine which files your application depends upon (LOCAL_SRC_FILES := main.c).
Tags: Define tags, as necessary (LOCAL_MODULE_TAGS := eng development).
Libraries: Define whether your application links with other libraries (LOCAL_SHARED_LIBRARIES := cutils).
Template file: Include a template file to define underlining make tools for a particular target (include $(BUILD_EXECUTABLE)).
《Android 内核剖析》 第18章 Android 编译系统
Make based
目标:条件
.PHONY 声明目标
Android 源码文件结构
drwxr-xr-x 6 lucas staff 306 Mar 4 12:37 .repo
-r--r--r-- 1 lucas staff 87 Oct 1 00:56 Makefile
drwxr-xr-x 3 lucas staff 136 Oct 15 08:17 abi
drwxr-xr-x 10 lucas staff 476 Oct 15 08:17 bionic
drwxr-xr-x 5 lucas staff 170 Mar 1 13:23 bootable
drwxr-xr-x 7 lucas staff 442 Mar 4 12:00 build
drwxr-xr-x 11 lucas staff 612 Mar 4 12:00 cts
drwxr-xr-x 17 lucas staff 782 Mar 4 12:00 dalvik
drwxr-xr-x 18 lucas staff 714 Nov 25 17:30 development
drwxr-xr-x 11 lucas staff 374 Mar 4 11:29 device
drwxr-xr-x 3 lucas staff 136 Oct 15 08:17 docs
drwxr-xr-x 164 lucas staff 5610 Mar 4 12:01 external
drwxr-xr-x 15 lucas staff 510 Jan 25 14:49 frameworks
drwxr-xr-x 10 lucas staff 442 Oct 1 01:01 gdk
drwxr-xr-x 12 lucas staff 408 Nov 25 17:32 hardware
drwxr-xr-x 12 lucas staff 680 Mar 4 12:01 libcore
drwxr-xr-x 4 lucas staff 306 Jan 28 02:08 libnativehelper
drwxr-xr-x 8 lucas staff 714 Jan 28 02:08 ndk
drwxr-xr-x 4 lucas staff 238 Mar 7 21:57 out
drwxr-xr-x 8 lucas staff 272 Nov 25 17:33 packages
drwxr-xr-x 5 lucas staff 238 Nov 25 17:33 pdk
drwxr-xr-x 11 lucas staff 374 Mar 4 12:00 prebuilts
drwxr-xr-x 53 lucas staff 2006 Jan 28 02:09 sdk
drwxr-xr-x 9 lucas staff 306 Oct 1 01:04 system
drwxr-xr-x 3 lucas staff 102 Mar 4 12:00 tools
Make 系统
编译命令
三张方式:
编译整个Android系统
```bash
source ./build/envsetup.sh
make PRODUCT-ful-crespo-eng
NDK is short for Native Development Kit. It does not benefit for all tasks. Typical good candidates for the NDK are self-contained, CPU-intensive operations that don’t allocate much memory, such as signal processing, physics simulation, and so on.
Add /Users/lucas/dev/android/android-ndk-r8d to ~/.bash_profile
Here’s the general outline of how you work with the NDK tools:
Place your native sources under /jni/...
Create /jni/Android.mk to describe your native sources to the NDK build system
Optional: Create /jni/Application.mk.
Build your native code by running the ‘ndk-build’ script from your project’s directory. It is located in the top-level NDK directory:
cd /ndk-build
The build tools copy the stripped, shared libraries needed by your application to the proper location in the application’s project directory.
Finally, compile your application using the SDK tools in the usual way. The SDK build tools will package the shared libraries in the application’s deployable .apk file.
On ARM emualtor
root@android:/data/data/com.example.hellojni/lib # ls
gdbserver
libhello-jni.so
root@android:/data/data/com.example.hellojni/lib #
Running JNI app on Intel Emulator.
# pwd
/data/data/com.example.hellojni/lib
# ls#
I/ActivityManager( 1013): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.example.hellojni/.HelloJni u=0} from pid 1151
D/dalvikvm( 1013): GC_FOR_ALLOC freed 376K, 9% free 12167K/13255K, paused 6ms, total 8ms
D/dalvikvm( 1800): Not late-enabling CheckJNI (already on)
I/dalvikvm( 1800): Turning on JNI app bug workarounds for target SDK version 3...
I/ActivityManager( 1013): Start proc com.example.hellojni for activity com.example.hellojni/.HelloJni: pid=1800 uid=10071 gids={1015, 1028}
E/Trace ( 1800): error opening trace file: No such file or directory (2)
W/dalvikvm( 1800): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lcom/example/hellojni/HelloJni;
W/dalvikvm( 1800): Class init failed in newInstance call (Lcom/example/hellojni/HelloJni;)
D/AndroidRuntime( 1800): Shutting down VM
W/dalvikvm( 1800): threadid=1: thread exiting with uncaught exception (group=0xb3fb7288)
E/AndroidRuntime( 1800): FATAL EXCEPTION: main
E/AndroidRuntime( 1800): java.lang.ExceptionInInitializerError
E/AndroidRuntime( 1800): at java.lang.Class.newInstanceImpl(Native Method)
E/AndroidRuntime( 1800): at java.lang.Class.newInstance(Class.java:1319)
E/AndroidRuntime( 1800): at android.app.Instrumentation.newActivity(Instrumentation.java:1053)
E/AndroidRuntime( 1800): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1974)
E/AndroidRuntime( 1800): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
E/AndroidRuntime( 1800): at android.app.ActivityThread.access$600(ActivityThread.java:130)
E/AndroidRuntime( 1800): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
E/AndroidRuntime( 1800): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 1800): at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime( 1800): at android.app.ActivityThread.main(ActivityThread.java:4745)
E/AndroidRuntime( 1800): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 1800): at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime( 1800): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
E/AndroidRuntime( 1800): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
E/AndroidRuntime( 1800): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 1800): Caused by: java.lang.UnsatisfiedLinkError: Couldn't load hello-jni: findLibrary returned null
E/AndroidRuntime( 1800): at java.lang.Runtime.loadLibrary(Runtime.java:365)
E/AndroidRuntime( 1800): at java.lang.System.loadLibrary(System.java:535)
E/AndroidRuntime( 1800): at com.example.hellojni.HelloJni.<clinit>(HelloJni.java:64)
E/AndroidRuntime( 1800): ... 15 more
W/ActivityManager( 1013): Force finishing activity com.example.hellojni/.HelloJni
D/dalvikvm( 1013): GC_FOR_ALLOC freed 43K, 7% free 12376K/13255K, paused 7ms, total 7ms
W/ActivityManager( 1013): Activity pause timeout for ActivityRecord{b4919ef8 com.example.hellojni/.HelloJni}
I/ActivityManager( 1013): No longer want com.android.email (pid 1375): hidden #16
I/Process ( 1800): Sending signal. PID: 1800 SIG: 9
I/ActivityManager( 1013): Process com.example.hellojni (pid 1800) has died.
W/InputMethodManagerService( 1013): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@b49f4280 attribute=android.view.inputmethod.EditorInfo@b4b3d0f0
W/ActivityManager( 1013): Activity destroy timeout for ActivityRecord{b4919ef8 com.example.hellojni/.HelloJni}
Sample “NativeActivity”
On ARM emulator
W/dalvikvm( 956): threadid=1: thread exiting with uncaught exception (group=0x40a70930)
E/AndroidRuntime( 956): FATAL EXCEPTION: main
E/AndroidRuntime( 956): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.native_activity/android.app.NativeActivity}: java.lang.IllegalArgumentException: Unable to find native library: native-activity
E/AndroidRuntime( 956): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180)
E/AndroidRuntime( 956): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
E/AndroidRuntime( 956): at android.app.ActivityThread.access$600(ActivityThread.java:141)
E/AndroidRuntime( 956): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
E/AndroidRuntime( 956): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 956): at android.os.Looper.loop(Looper.java:137)
E/AndroidRuntime( 956): at android.app.ActivityThread.main(ActivityThread.java:5039)
E/AndroidRuntime( 956): at java.lang.reflect.Method.invokeNative(Native Method)
E/AndroidRuntime( 956): at java.lang.reflect.Method.invoke(Method.java:511)
E/AndroidRuntime( 956): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
E/AndroidRuntime( 956): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
E/AndroidRuntime( 956): at dalvik.system.NativeStart.main(Native Method)
E/AndroidRuntime( 956): Caused by: java.lang.IllegalArgumentException: Unable to find native library: native-activity
E/AndroidRuntime( 956): at android.app.NativeActivity.onCreate(NativeActivity.java:181)
E/AndroidRuntime( 956): at android.app.Activity.performCreate(Activity.java:5104)
E/AndroidRuntime( 956): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080)
E/AndroidRuntime( 956): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144)
E/AndroidRuntime( 956): ... 11 more
W/ActivityManager( 285): Force finishing activity com.example.native_activity/android.app.NativeActivity
W/Trace ( 402): Unexpected value from nativeGetEnabledTags: 0
D/dalvikvm( 285): GC_FOR_ALLOC freed 467K, 21% free 11175K/14060K, paused 89ms, total 90ms
Most intelligent people research in Physics, the second most research in Math, and the third most (actually, the least) research in computer.
And in order to hide their inability, they attach science to computer, call it COMPUTER SCIENCE. We know it is just engineering.
In order to mask their intelligent disability, computer scientists have seperated the original simple thing into many complicated branches, and wrote many obscure books to describe their ideas. Good books are so scarce that every student find it hard to understand the very basic principles of computer science. Actually, computer science is simple, as simple as the book Computer Systems: A Programmer’s Perspective tells, but not less.
Computer Systems: A Programmer’s Perspective is such a good book that every one who wants to program should read. The two authors, Randal E. Bryant and David R. O’Hallaron are professors from Carnegie Mellon University. The language is straightforward and the authors’ explanations are incisive.
Anyway, this is a MUST-READ book for CS student.
Why read this book?
In fact, long long time ago, when I was a sophormore student studying software enginnering, I heard the book. But at that time, the book is too thick (over 1000 pages) for me, and English is a barrier. Until recently, when I read a book called Dark Time, I did not have the chance to recall the book. In Dark Time, the author mentions a concept KNOWLEDGE STRUCTURE. Put it simply, it means the best way to learn a field is to know the overall structure, but not many single isolated points. I highly agree with this idea. CSAPP is such a book that can help you understand the overall blueprint for computer science, or at least, programming.
Another reason I am planning to read this book is that it acts as a display stand for too long time on my colleagues desk, similar fate as “The Art of Computer Programming”. Then one day, I think I should change its fate. It should not only work as a stand, but enrich my understanding in computer programming.
In addition, in the last recent year, as I encountered bottleneck when solving framework or system level issues on Android, I get intense desire to totally understand how computer program works. So one of my expectations from reading the book is to improve my ability to handle system level design and development.
The last enchanting point is from the fame of the authors. They are from CMU, standing for the best computer programming education and research. So it should worth to read.
Knowledge Structure (GRAPH)
COMPUTER SCIENCE
Programming
Language
Use
Design
Design Pattern
Algorithms
Data Structures
Software Engineering
System Design
Parallel/Concurrency
Distributed System
Hardware
Security
Human Computer Interaction
UX
UI
Statistical Machine Learning
Data Mining
Artificial Intelligence
What to expect to learn from the book?
Computer science knowledge structure
hardware, software, OS, how program works, system design, etc.
In this project, we investigate the possible options for users to restrict application permissions in a more
ne-grained way and provide a proof of concept for a command-line tool that can remove permissions from
applications before installation. We tested our tool with Android 2.2 (Froyo) on an emulated NexusS and a
real Samsung Galaxy S GT-I9000.
Problem
Android OS provides zero-one solution, but users require more fine grained control over application permission:
selectively accept permissions during installation process.
revoke permission after application is installed.
Existing solution
no perfect solution
Google Play: permission management apps, like privacy blocker, PDroid Privacy Protection, etc.
drawback: not very effective, crashes sometime, some blocking apps require root
os level trust for blocking applicaiton
Our solution
modify the app before installation
pros: no more trust than the app itself
cons:
approach: reverse engineering
1. Unzip apk package
2. Remove permissions from AndroidManifest.xml
3. Modify application code to make sure it doesn’t crash because of permission issues
4. Zip modied apk package
5. Run on phone
fads
#Implementation
apktool -> smali, debugging is impractical, no enough material
dex2jar -> java, recompiling is difficult.
implement a class which extends the class whose function requires the permission we are removing.
does not work with final classes, such as LocationManager.
replace all API call with a dummy static method call.
resources
stowaway
permission map: android-permissions.org, tells which permissions each API call requires.
Java Collections Framework by John Zukowski. This short document(44 pages) gives a clear overview of Collection Framework. Also it provides some usage examples and exercises. Strongly recommeded.
How to selectively accept permission list required by the applicaiton for user?
Android Security architecture overview.
Pros and cons analysis.
On Google groups for Android AOSP, there is a issue requesting for the feature.
The major concern from Android Product team is the complexity of writing applicaitons. Bullshit.
@Overridepublicint[]getPackageGids(StringpackageName)throwsNameNotFoundException{try{int[]gids=mPM.getPackageGids(packageName);if(gids==null||gids.length>0){returngids;}}catch(RemoteExceptione){thrownewRuntimeException("Package manager has died",e);}thrownewNameNotFoundException(packageName);}
@OverridepublicPackageManagergetPackageManager(){if(mPackageManager!=null){returnmPackageManager;}IPackageManagerpm=ActivityThread.getPackageManager();if(pm!=null){// Doesn't matter if we make more than one instance.return(mPackageManager=newApplicationPackageManager(this,pm));}returnnull;}
publicint[]getPackageGids(StringpackageName){finalbooleanenforcedDefault=isPermissionEnforcedDefault(READ_EXTERNAL_STORAGE);// reader synchronized(mPackages){PackageParser.Packagep=mPackages.get(packageName);if(DEBUG_PACKAGE_INFO)Log.v(TAG,"getPackageGids"+packageName+": "+p);if(p!=null){finalPackageSettingps=(PackageSetting)p.mExtras;finalSharedUserSettingsuid=ps.sharedUser;int[]gids=suid!=null?suid.gids:ps.gids;// include GIDs for any unenforced permissionsif(!isPermissionEnforcedLocked(READ_EXTERNAL_STORAGE,enforcedDefault)){finalBasePermissionbasePerm=mSettings.mPermissions.get(READ_EXTERNAL_STORAGE);gids=appendInts(gids,basePerm.gids);}returngids;}}// stupid thing to indicate an error.returnnewint[0];}
A mind map is a diagram used to visually outline information. A mind map is often created around a single word or text, placed in the center, to which associated ideas, words and concepts are added. Major categories radiate from a central node, and lesser categories are sub-branches of larger branches.[1] Categories can represent words, ideas, tasks, or other items related to a central key word or idea.
Mindmaps can be drawn by hand, either as “rough notes” during a lecture or meeting, for example, or as higher quality pictures when more time is available. An example of a rough mind map is illustrated.
Other terms for this diagramming style are: “spider diagrams,” “spidergrams,” “spidergraphs,” “webs”, “mind webs”, or “webbing”, and “idea sun bursting”.[2][3] (A “spider diagram” used in mathematics and logic is different.)
mBS Mobile is a carrier grade embedded OSGi solution integrated and optimized for a variety of smart phone platforms. It is built over ProSyst’s award winning and certified OSGi implementation mBedded Server Professional Edition. mBS Mobile provides highest availability, scalability and reliability and sets new standards for performance and memory efficiency through several unique technology innovations.
Fully compliant to OSGi R4 Core, OSGi R4 Mobile and JSR 232 specifications
Tightly integrated with target operating systems Contains fully featured remote management agent (based on OMA-DM)
Adds platform support for Web Widgets (i.e. W3C, JIL, Opera, Bondi) applications
Full security support with dynamic, manageable device policy
Perfomance and footprint optimized
For more information, see http://www.prosyst.com/index.php/de/html/content/46/Mobile-OSGi-Runtimes/.
‘
Pencil Project
A collection of screenshots encompassing some of the best, most beautiful looking Android apps.
Aiming to provide inspiration and insight into Android UI conventions.
android-ui-utils
Utilities that help in the design and development of Android application user interfaces. This library currently consists of three individual tools for designers and developers:
Open source projects to facilitate Android design and development
Android Bootstrap
Bootstrap your next Android Application
Android Bootstrap includes a full working implementation of Fragments, Fragment Pager, Account Manager, android-maven-plugin, RoboGuice 2, ActionBarSherlock 4, ViewPagerIndicator, http-request, GSON, Robotium for integration testing, API Consumption with an API on Parse.com and much more.
HoloEverywhere
Bringing Holo Theme from Android 4.0 to 1.6 and above.
Android projects on Github is a community on Google+, which provides collections of nice Android open source projects.
AdBlock for Chrome! Block all advertisements on all web pages, even Facebook, Youtube, and Hulu.
It works automatically: just click “Add to Chrome,” then visit your favorite website and see the ads disappear!
To provide website visitors more choice on how their data is collected by Google Analytics, we have developed the Google Analytics Opt-out Browser Add-on. The add-on communicates with the Google Analytics JavaScript to indicate that information about the website visit should not be sent to Google Analytics.
If you want to opt-out, download and install the add-on for your current web browser. The Google Analytics Opt-out Browser Add-on is available for Microsoft Internet Explorer, Google Chrome, Mozilla Firefox, Apple Safari and Opera.
Nowadays, may 2C software are using a free-ads mode. For end-user, the software costs no money at all. The usability and professioness of these software applications are as good as commercial products, or even better, for example, QQ, SOGOU Input Method,etc.
try{// code that may break//...}catch(Exceptione){// to handle the exception here. // Usaually, just print out the exception stacktrace. e.printStackTrace();}
一个简单的例子
下面的代码演示了try catch 的简单使用:
Stringfilename="/nosuchdir/myfilename";try{// Create the filenewFile(filename).createNewFile();}catch(IOExceptione){// Print out the exception that occurredSystem.out.println("Unable to create "+filename+": "+e.getMessage());}
public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
Set the default handler invoked when a thread abruptly terminates due to an uncaught exception, and no other handler has been defined for that thread.
Uncaught exception handling is controlled first by the thread, then by the thread’s ThreadGroup object and finally by the default uncaught exception handler. If the thread does not have an explicit uncaught exception handler set, and the thread’s thread group (including parent thread groups) does not specialize its uncaughtException method, then the default handler’s uncaughtException method will be invoked.
By setting the default uncaught exception handler, an application can change the way in which uncaught exceptions are handled (such as logging to a specific device, or file) for those threads that would already accept whatever “default” behavior the system provided.
Note that the default uncaught exception handler should not usually defer to the thread’s ThreadGroup object, as that could cause infinite recursion.
Parameters:
eh - the object to use as the default uncaught exception handler. If null then there is no default handler.
Throws:
SecurityException - if a security manager is present and it denies RuntimePermission (“setDefaultUncaughtExceptionHandler”)
Since:
1.5
When implementing ACRA, I have never been able to start a new Activity after receiving an uncaught exception. It looks like the whole process is switched by the system to a special state preventing him from allowing any new resource.
The only option I have found for the moment is to send a status bar notification which is kept by the system after the application being restarted. The notification then triggers an intent for a dialog activity when the user selects it.
Alexey Yakovlev studied in much more details this issue and came to the conclusion that there could be a chance of triggering a new activity when the crash occurs on a thread which is not the UI thread. Though we did not find a simple enough workaround to start directly an activity in all cases.
I got rid of the default force close dialog by killing the process myself without invoking the orginial default uncaught exception handler.
There are a number of locations to configure settings for Jetty and web applciations running on it.
A number of configuration files can be found at ${jetty.home}/start.ini.
$java -jar start.jar --help
Available Configurations:
By convention, configuration files are kept in $JETTY_HOME/etc.
The known configuration files are:
etc/jetty-ajp.xml
etc/jetty-annotations.xml
etc/jetty-bio-ssl.xml
etc/jetty-bio.xml
etc/jetty-contexts.xml
etc/jetty-debug.xml
etc/jetty-deploy.xml
etc/jetty-fileserver.xml
etc/jetty-ipaccess.xml
etc/jetty-jmx.xml
etc/jetty-logging.xml
etc/jetty-monitor.xml
etc/jetty-overlay.xml
etc/jetty-plus.xml
etc/jetty-policy.xml
etc/jetty-proxy.xml
etc/jetty-requestlog.xml
etc/jetty-rewrite.xml
etc/jetty-spdy-proxy.xml
etc/jetty-spdy.xml
etc/jetty-ssl.xml
etc/jetty-stats.xml
etc/jetty-testrealm.xml
etc/jetty-webapps.xml
etc/jetty-xinetd.xml
etc/jetty.xml
Defaults:
A start.ini file may be used to specify default arguments to start.jar,
which are used if no command line arguments are provided and override
the defaults in the start.config file. If the directory jetty.home/start.d
exists, then multiple *.ini files will be read from that directory in
alphabetical order. If --ini options are provided on the command line,
then start.ini and start.d will NOT be read.
The current start.ini arguments are:
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations
etc/jetty.xml
etc/jetty-annotations.xml
etc/jetty-deploy.xml
etc/jetty-webapps.xml
etc/jetty-contexts.xml
etc/jetty-testrealm.xml
Contexts
An important concept in Jetty is Context. A web application is a context. Thus each xml file configured under ./contexts directorty represents a web application.
./contexts/test.xml
The applicaiton configure file, using format of jetty. Jetty uses its own IOC mechanism to read and inject components to the server. The root element of the file is Server, representing a server.
web.xml [webapp_name]/WEB-INF/web.xml. This file is located under a web app WEB-INF directory. The directory name WEB-INF and file name web.xml is regulated by Serverlet web app standards.
jetty.xml
mac:jetty-distribution-8.1.5.v20120716 user1$ cat start.
cat: start.: No such file or directory
lucas-mac:jetty-distribution-8.1.5.v20120716 lucas$ cat start.ini
#===========================================================
# Jetty start.jar arguments
# Each line of this file is prepended to the command line
# arguments # of a call to:
# java -jar start.jar [arg...]
#===========================================================
#===========================================================
# If the arguements in this file include JVM arguments
# (eg -Xmx512m) or JVM System properties (eg com.sun.???),
# then these will not take affect unless the --exec
# parameter is included or if the output from --dry-run
# is executed like:
# eval $(java -jar start.jar --dry-run)
#
# Below are some recommended options for Sun's JRE
#-----------------------------------------------------------
# --exec
# -Dorg.apache.jasper.compiler.disablejsr199=true
# -Dcom.sun.management.jmxremote
# -Dorg.eclipse.jetty.util.log.IGNORED=true
# -Dorg.eclipse.jetty.LEVEL=DEBUG
# -Dorg.eclipse.jetty.util.log.stderr.SOURCE=true
# -Xmx2000m
# -Xmn512m
# -verbose:gc
# -XX:+PrintGCDateStamps
# -XX:+PrintGCTimeStamps
# -XX:+PrintGCDetails
# -XX:+PrintTenuringDistribution
# -XX:+PrintCommandLineFlags
# -XX:+DisableExplicitGC
# -XX:+UseConcMarkSweepGC
# -XX:ParallelCMSThreads=2
# -XX:+CMSClassUnloadingEnabled
# -XX:+UseCMSCompactAtFullCollection
# -XX:CMSInitiatingOccupancyFraction=80
#-----------------------------------------------------------
#===========================================================
# Start classpath OPTIONS.
# These control what classes are on the classpath
# for a full listing do
# java -jar start.jar --list-options
#-----------------------------------------------------------
OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations
#-----------------------------------------------------------
#===========================================================
# Configuration files.
# For a full list of available configuration files do
# java -jar start.jar --help
#-----------------------------------------------------------
#etc/jetty-jmx.xml
etc/jetty.xml
etc/jetty-annotations.xml
# etc/jetty-ssl.xml
# etc/jetty-requestlog.xml
etc/jetty-deploy.xml
#etc/jetty-overlay.xml
etc/jetty-webapps.xml
etc/jetty-contexts.xml
etc/jetty-testrealm.xml
#===========================================================
For one of my project, it took long time for me to choose which technology stack I would use for the server. Finally I did not choose popular frameworks like ruby, scala, etc. I chose Java/ JSP soly because the technology is mature and I was lucky to have some experience playing with it. Though not a experienced Java server developer, I think my knowledge of Java should be sufficent to kick off.
Surfing the internet, I prefer Jetty + nginx over tomcat as the previous compostion provides much better concurrency performance.
Things get complex when I start to write code.
First thing is to configure the dev and deploy enviroment. This was a hard time.
Ocassionally we find our android emulators cannot connect to the internet. May it be working yesterday but not today. Damn it!
The explanation stated by this post reveils the underlying cause.
On some WI-FI network, the emulator could not get DNS configuration automatically. My experience shows this not only happenes to wifi netowrk, but also to cable network.
Solution
There are 2 solutions:
Manually set DNS,
set DNS when emulator starts up.
In Eclipse:
Window>Preferences>Android>Launch
Default emulator options: -dns-server 8.8.8.8,8.8.4.4
set DNS after emulator starts up.
setprop dns.net1 8.8.8.8
The IP 8.8.8.8 and 8.8.4.4 are provided by Google freely.
Toggle the Internet access to your emulator with F8 (on Windows) and Fn + F8 (on Mac OS X).
With this shortcut, you get the ACTION_BACKGROUND_DATA_SETTING_CHANGED dispatched.
One important characteristic of a framework is that the methods defined by the user to tailor the framework will often be called from within the framework itself, rather than from the user’s application code. The framework often plays the role of the main program in coordinating and sequencing application activity. This inversion of control gives frameworks the power to serve as extensible skeletons. The methods supplied by the user tailor the generic algorithms defined in the framework for a particular application.
importandroid.app.Activity;importandroid.content.Context;importandroid.os.Bundle;importandroid.telephony.TelephonyManager;importandroid.util.Log;publicclassAndroidNumberActivityextendsActivity{privatestaticfinalStringTAG="AndroidNumberActivity";/** Called when the activity is first created. */@OverridepublicvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);// 创建电话管理TelephonyManagertm=(TelephonyManager)// 与手机建立连接getSystemService(Context.TELEPHONY_SERVICE);// 获取手机号码StringphoneNo=tm.getLine1Number();Log.d(TAG,"phnone NO: "+phoneNo);}}
Day and day ago, I wrote a DownloadingService for Android application. The service is provided in an Android SDK, which ill be utilized by other android apps. Some app developers wrote their code in such as way that when user press BACK button on Android, the app will kill itself in such a way:
As an advanced Android developer, you may think how this could be stupid and violate the design principle of Android Framework, i.e., “Activity” should follow it’s own lifecycle.
But as the old saying said, customer is our God. SDK users, some could be novice android develoeprs, they refuse to change their existing strucutre and insist to kill the process when back button is pressed. As a result, the “DownloadingService” is also killed as it resides in the app’s main proces.
Though the phenomenon is expected, the novice developer does not think so. They want the DownloadingService to continue download files from the Internet even though they killed the main app process. To make a concession, I changed the “DownloadingSercie” so it runs in a separate process.
This is fair easy. Developer only has to mark the service as “exported” in “AndroidManifest.xml”.
One developer reported under the scenario he created, the DownloadingService could not correctly download the file:
Start the app, click download
Exit the app using “BACK” button, which kills app’s main process.
Restart the app, click download (the same url as the Step 1). He observes that even though the url is the same as Step 1, there will be another notification bar item.
The first download task is still excuting (from the notification bar).
This is absolutely unexpected, as I added code for this kind of duplicate check. If the url is in downloaing task list, it will not be downloaded again. DownloadingService generates a file name based on url (MD5 hash of the url) along with the file extension, plus suffix “.tmp”, then saves it to SD card. After the file is complete downloaded, the file will be renamed to remove suffix “.tmp”. Here the duplicate check code did not work!
Here it is:
/**
* Check if the download request is already processed and is in downloading.
*
* @param item
* @return
*/privatestaticbooleanisInDownloadList(DownloadItemitem){if(mClients==null)returnfalse;for(DownloadItemd:mClients.keySet()){if(d.mUrl.equals(item.mUrl)){returntrue;}}returnfalse;}
mClients is declared as DownloadingService’s class member:
// The clients connected to the service. Messenger references the client,// where DownloadItem specifies the information required by this service.privateMap<DownloadAgent.DownloadItem,Messenger>mClients=newHashMap<DownloadAgent.DownloadItem,Messenger>();
mClient as a Map records all downloading url. If a url is in downloading, it can’t be downloaded again. So no reason that a duplicate url can be added to mClients. It is so strange.
Each single line of code looks fine to me.
Then I launch the debug mode, for both the main process and DownloadingService process.
@OverridepublicvoidonCreate(){super.onCreate();Log.d(LOG_TAG,"onCreate ");// uncomment the following line to enable inter-process debug.android.os.Debug.waitForDebugger();// ...}
The trace tells that in Step 3, mClients is empty: no elements is added before. This is so abnormal. What happened?
Obviously the Process is still living there, otherwise, the first task cannot continue executing. Recalling from Android API description, the service runs in the main thread of its process. I doubt if the service was destroyed and re-created. After adding log information in it’s onCreate and onDestroy method, yes! The service was destroyed and re-created again. But the process id and thread id are the same.
I was really confused. It seems Step 1 and Step 3 were in the same thread. But if that’s the case, why data stored in ‘mClients’ is losed?
Hinted by the number ‘1’, I guess the thread might be killed and re-created, but the thread number was reused as OS find the number can be re-allocated the second time as it realize that no other thread is using this id.
So to make the intestigation a further step, I added a member
classDownloadingServiceextendsService{// generate an random id each time a class instance is created. In this case, it means a new thread is created. intthread_random_id=newRandom().nextInt(10000);// ....}
A-ha, thread_random_id is different for the 2 cases!
DownloadingService is killed and re-created!
The first task is still downloading because the task was launched in a different thread other than the Service thread itself.
The other day I wrote an Android downloading service running in a separate process. The main application process will send command/message to the downloading process asking it to download item. The downloading process is supposed to post back the downloading process and status to the main applicaiton process by sending messages. Everything works fine: the application process can send command and downloading process can receive the command and then start downloading the item. Also the application process can also receives the process update messages from the donwloading process.
For the sake of recursive testing, namely, I wrote the following test case, following the direction specified by Android SDK documentation.
Then run in Eclipse, everything worked fine, and I didnot put much effort on it, it got into the source depot.
Days later, one of my collegues read that code, run it, but asked: “How do you know functions onProgressUpdate and onEnd are called?”. Inspired by his question, I updated the code as:
Here I add 3 class variables onStartTriggered, onProgressUpdateTriggered, onEndTriggered to track whether each method is successfully called. And in try catch block, use Assert.assertTrue to make sure these variables were accessed by correspondent methods after signal.await call. But unfortunately, this time I am not lucky enough. The test case failed.
After digging into the code, I found these 3 methods were not called at all. That was tricky, they can be successfuly called in the applicaiton code, but not in the test!
With a few Google search, I found this diagram on StackOverflow. Thread: Intent resolved to different process when running Unit Test in Android
The diagram explained the phenomenon to some extent.
The test runner is running in a sparate thread than the UI thread. What this hint is that the test runner thread has no looper, which is required for Android message communication mechanism. See andorid.os.Looper.
As it infers, the runner thread has no looper, thus IDownloadListener listener, which was implemented using the Messager in android to receive messages from downloading process, cannot receives message. As a result, in the test code, the above 3 methods were not called.
But then how can we test that code? The solution is simple, move the listener code to UI thread. In fact, Android Test framework does support this. Testing on the UI thread.
Caution:
Although Testing on the UI thread tells that you can use both @UIThreadTest annotation and mActivity.runOnUiThread(new Runnable() to run some code on UI thread, in the case I mentioned above, do not use the first solution.
Our code use
signal.await(300,TimeUnit.SECONDS);
to synchronize. So if we put @UIThreadTest annotation, listener will not be executed, as await will block the main UI thread. The Looper is blocked, it cannot receive messages!.
Android contact API uses Content Provider to provide data for application. The service is provided by com.android.providers.contacts. All the contact data are stored in SQLite database. You can find all the data under /data/data/com.android.providers.contacts/databases, as following:
The most important and the central database here is contacts2.db. To view what’s in this database, you can utilize the tool called sqlite3 provided by Google. This is the link to the official description of the tool. This tool is under /system/bin/ by default in Android. But in case you cannot find it on you device, you can use Eclipse SQLite Plugin to visualize the table structure and data. See this article Browse an Android Emulator SQLite Database in Eclipsefor help.
sqlite> .schema contacts
CREATE TABLE contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,name_raw_contact_id INTEGER REFERENCES raw_contacts(_id),photo_id INTEGER REFERENCES data(_id),photo_file_id INTEGER REFERENCES photo_files(_id),custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,has_phone_number INTEGER NOT NULL DEFAULT 0,lookup TEXT,company TEXT,nickname TEXT,contact_account_type TEXT,status_update_id INTEGER REFERENCES data(_id));
CREATE INDEX contacts_has_phone_index ON contacts (has_phone_number);
CREATE INDEX contacts_name_raw_contact_id_index ON contacts (name_raw_contact_id);
sqlite> .schema raw_contacts
CREATE TABLE raw_contacts (_id INTEGER PRIMARY KEY AUTOINCREMENT,account_name STRING DEFAULT NULL, account_type STRING DEFAULT NULL, data_set STRING DEFAULT NULL, sourceid TEXT,raw_contact_is_read_only INTEGER NOT NULL DEFAULT 0,version INTEGER NOT NULL DEFAULT 1,dirty INTEGER NOT NULL DEFAULT 0,deleted INTEGER NOT NULL DEFAULT 0,contact_id INTEGER REFERENCES contacts(_id),aggregation_mode INTEGER NOT NULL DEFAULT 0,aggregation_needed INTEGER NOT NULL DEFAULT 1,custom_ringtone TEXT,send_to_voicemail INTEGER NOT NULL DEFAULT 0,times_contacted INTEGER NOT NULL DEFAULT 0,last_time_contacted INTEGER,starred INTEGER NOT NULL DEFAULT 0,display_name TEXT,display_name_alt TEXT,display_name_source INTEGER NOT NULL DEFAULT 0,phonetic_name TEXT,phonetic_name_style TEXT,sort_key TEXT COLLATE PHONEBOOK,sort_key_alt TEXT COLLATE PHONEBOOK,sort_key_custom TEXT COLLATE PHONEBOOK,name_verified INTEGER NOT NULL DEFAULT 0,sync1 TEXT, sync2 TEXT, sync3 TEXT, sync4 TEXT );
CREATE INDEX raw_contact_sort_key1_index ON raw_contacts (sort_key);
CREATE INDEX raw_contact_sort_key2_index ON raw_contacts (sort_key_alt);
CREATE INDEX raw_contact_sort_key_custom_index ON raw_contacts (sort_key_custom);
CREATE INDEX raw_contacts_contact_id_index ON raw_contacts (contact_id);
CREATE INDEX raw_contacts_source_id_data_set_index ON raw_contacts (sourceid, account_type, account_name, data_set);
CREATE INDEX raw_contacts_source_id_index ON raw_contacts (sourceid, account_type, account_name);
CREATE TRIGGER raw_contacts_deleted BEFORE DELETE ON raw_contacts BEGIN DELETE FROM data WHERE raw_contact_id=OLD._id; DELETE FROM agg_exceptions WHERE raw_contact_id1=OLD._id OR raw_contact_id2=OLD._id; DELETE FROM visible_contacts WHERE _id=OLD.contact_id AND (SELECT COUNT(*) FROM raw_contacts WHERE contact_id=OLD.contact_id )=1; DELETE FROM default_directory WHERE _id=OLD.contact_id AND (SELECT COUNT(*) FROM raw_contacts WHERE contact_id=OLD.contact_id )=1; DELETE FROM contacts WHERE _id=OLD.contact_id AND (SELECT COUNT(*) FROM raw_contacts WHERE contact_id=OLD.contact_id )=1; END;
CREATE TRIGGER raw_contacts_marked_deleted AFTER UPDATE ON raw_contacts BEGIN UPDATE raw_contacts SET version=OLD.version+1 WHERE _id=OLD._id AND NEW.deleted!= OLD.deleted; END;
sqlite> .schema data
CREATE TABLE data (_id INTEGER PRIMARY KEY AUTOINCREMENT,package_id INTEGER REFERENCES package(_id),mimetype_id INTEGER REFERENCES mimetype(_id) NOT NULL,raw_contact_id INTEGER REFERENCES raw_contacts(_id) NOT NULL,is_read_only INTEGER NOT NULL DEFAULT 0,is_primary INTEGER NOT NULL DEFAULT 0,is_super_primary INTEGER NOT NULL DEFAULT 0,data_version INTEGER NOT NULL DEFAULT 0,data1 TEXT,data2 TEXT,data3 TEXT,data4 TEXT,data5 TEXT,data6 TEXT,data7 TEXT,data8 TEXT,data9 TEXT,data10 TEXT,data11 TEXT,data12 TEXT,data13 TEXT,data14 TEXT,data15 TEXT,data_sync1 TEXT, data_sync2 TEXT, data_sync3 TEXT, data_sync4 TEXT );
CREATE INDEX data_mimetype_data1_index ON data (mimetype_id,data1);
CREATE INDEX data_raw_contact_id ON data (raw_contact_id);
CREATE TRIGGER data_deleted BEFORE DELETE ON data BEGIN UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; DELETE FROM phone_lookup WHERE data_id=OLD._id; DELETE FROM status_updates WHERE status_update_data_id=OLD._id; DELETE FROM name_lookup WHERE data_id=OLD._id; END;
CREATE TRIGGER data_updated AFTER UPDATE ON data BEGIN UPDATE data SET data_version=OLD.data_version+1 WHERE _id=OLD._id; UPDATE raw_contacts SET version=version+1 WHERE _id=OLD.raw_contact_id; END;
To visiualize the table, here is the screen short from Eclipse SQlite Plugin:
| Column Name | example |description |
|:———–|:———–|:————:|
|long _ID ||Row ID. Sync adapters should try to preserve row IDs during updates. In other words, it is much better for a sync adapter to update a raw contact rather than to delete and re-insert it.
|CONTACT_ID || read-only The ID of the row in the ContactsContract.Contacts table that this raw contact belongs to. Raw contacts are linked to contacts by the aggregation process, which can be controlled by the AGGREGATION_MODE field and ContactsContract.AggregationExceptions.
int AGGREGATION_MODE read/write A mechanism that allows programmatic control of the aggregation process. The allowed values are AGGREGATION_MODE_DEFAULT, AGGREGATION_MODE_DISABLED and AGGREGATION_MODE_SUSPENDED. See also ContactsContract.AggregationExceptions.
int DELETED read/write The “deleted” flag: “0” by default, “1” if the row has been marked for deletion. When delete(Uri, String, String[]) is called on a raw contact, it is marked for deletion and removed from its aggregate contact. The sync adaptor deletes the raw contact on the server and then calls ContactResolver.delete once more, this time passing the CALLER_IS_SYNCADAPTER query parameter to finalize the data removal.
int TIMES_CONTACTED read/write The number of times the contact has been contacted. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. After that, this value is typically updated via markAsContacted(ContentResolver, long).
long LAST_TIME_CONTACTED read/write The timestamp of the last time the contact was contacted. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. After that, this value is typically updated via markAsContacted(ContentResolver, long).
int STARRED read/write An indicator for favorite contacts: ‘1’ if favorite, ‘0’ otherwise. Changing this field immediately affects the corresponding aggregate contact: if any raw contacts in that aggregate contact are starred, then the contact itself is marked as starred.
String CUSTOM_RINGTONE read/write A custom ringtone associated with a raw contact. Typically this is the URI returned by an activity launched with the ACTION_RINGTONE_PICKER intent. To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted. To set a custom ringtone on a contact, use the field Contacts.CUSTOM_RINGTONE instead.
int SEND_TO_VOICEMAIL read/write An indicator of whether calls from this raw contact should be forwarded directly to voice mail (‘1’) or not (‘0’). To have an effect on the corresponding value of the aggregate contact, this field should be set at the time the raw contact is inserted.
String ACCOUNT_NAME read/write-once The name of the account instance to which this row belongs, which when paired with ACCOUNT_TYPE identifies a specific account. For example, this will be the Gmail address if it is a Google account. It should be set at the time the raw contact is inserted and never changed afterwards.
String ACCOUNT_TYPE read/write-once
The type of account to which this row belongs, which when paired with ACCOUNT_NAME identifies a specific account. It should be set at the time the raw contact is inserted and never changed afterwards.
To ensure uniqueness, new account types should be chosen according to the Java package naming convention. Thus a Google account is of type “com.google”.
String DATA_SET read/write-once
The data set within the account that this row belongs to. This allows multiple sync adapters for the same account type to distinguish between each others’ data. The combination of ACCOUNT_TYPE, ACCOUNT_NAME, and DATA_SET identifies a set of data that is associated with a single sync adapter.
This is empty by default, and is completely optional. It only needs to be populated if multiple sync adapters are entering distinct data for the same account type and account name.
It should be set at the time the raw contact is inserted and never changed afterwards.
String SOURCE_ID read/write String that uniquely identifies this row to its source account. Typically it is set at the time the raw contact is inserted and never changed afterwards. The one notable exception is a new raw contact: it will have an account name and type (and possibly a data set), but no source id. This indicates to the sync adapter that a new contact needs to be created server-side and its ID stored in the corresponding SOURCE_ID field on the phone.
int VERSION read-only Version number that is updated whenever this row or its related data changes. This field can be used for optimistic locking of a raw contact.
int DIRTY read/write Flag indicating that VERSION has changed, and this row needs to be synchronized by its owning account. The value is set to “1” automatically whenever the raw contact changes, unless the URI has the CALLER_IS_SYNCADAPTER query parameter specified. The sync adapter should always supply this query parameter to prevent unnecessary synchronization: user changes some data on the server, the sync adapter updates the contact on the phone (without the CALLER_IS_SYNCADAPTER flag) flag, which sets the DIRTY flag, which triggers a sync to bring the changes to the server.
String SYNC1 read/write Generic column provided for arbitrary use by sync adapters. The content provider stores this information on behalf of the sync adapter but does not interpret it in any way.
String SYNC2 read/write Generic column for use by sync adapters.
String SYNC3 read/write Generic column for use by sync adapters.
String SYNC4 read/write Generic column for use by sync adapters.