2012年5月17日 星期四

C# with Json.NET 多個Constructor

有一次我想要在C#中把object serialize成json然後存到硬碟。我選擇的是Json.NET這個套件。但當我要deserialize的時候卻發現轉不過來。

原因是當你的class有多個或沒有constructor的時候就會有問題。你必須至少有一個constructor而且假設有多個建構子的時候必須要在建構子的宣告上面加上[JsonConstructor]
例如
[JsonConstructor]
public sample(xxx a, yyy b)
{
    
}

Windows 8 Metro Style App Binding

Metro的binding真的有夠難懂。
事情是這樣的,假設你有一個資料結構 plan
public class PlanSource
{
    public ObservableCollection PlanList = new ObservableCollection();
}

public class Plan
{
    public string Name
    {
        set;
        get;
    }
    public ObservableCollection itemList=new ObservableCollection();
}

public class Item
{
    public string DisplayName
    {
        get;
        set;
    }
}

然後你有一個ListView,並且你想要顯示所有PlanList 裡面的Name你可以這樣設計你的ListView

    
    
    
        
            
                
                
            
        
    
    

然後可以在那一頁的cs檔(c#)裡面例如OnNavigatedTo 填上這段
PlanSource ps=new PlanSource();
protected override void OnNavigatedTo(NavigationEventArgs e)
{

    this.DataContext = ps;
}
這樣可以產生什麼效果呢?

  1. 如同一開始說的,這樣可以把PlanList裡面的plan的name顯示在list上
  2. 當資料更新的時候,List裡面的資料也會自己更新。要注意的是,因為我們是希望PlanList會自動更新,因此他必須是ObservableCollection的資料型態。
再進一步,假設同一頁中有另外一個ListView想要顯示PlanListView中被選擇項目的Item的DisplayName。舉例來說,假設PlanList裡面有兩個Plan的物件,他們的Name 分別是plan1 跟plan2,那這個時候PlanListView就會顯示plan1 和plan2 。假設plan1裡也有兩個item他們的DisplayName分別是 item1 跟item2因此我希望當選擇testplan1的時候另外一個ListView可以顯示item1跟item2 e.g. TestPlan Item plan1 ----item1 | --item2 plan2 利用binding可以快速地達到這個目的,我們只要這樣設計另外一個ListView如下
                                          

    
    
                
            
        
    
    

Windows 8 Metro Style App Decoration Design Pattern

目的是這樣的: 有多個頁面想要使用相同判斷邏輯的APP BAR
因此,原本想要使用class繼承button 的control 的方式但是會有error,所以後來想到的方法是在原本的frame裡面嵌一個frame假設叫frame1,然後只要把子頁面的東西顯示在frame1上就可以達到這目的了。

2012年4月18日 星期三

Android SurfaceFlinger Part 2-1 - Android View System

這裡要解釋的正式左邊的這張圖,一個app開起來之後不管是建立一般的物件或是使用skia這個2D的繪圖的library最後都是在放在surface上面。接下來要討論的就是在一個app建立之後view的建立、surface的建立和如何與surfaceflinger做溝通。


在Android的app來說,他的外表示靠Activity來建立的,因此我們就從Activity開始來看。當一個 app要被啟動是利用zygote去fork一個process來啟動。最先會跑到 main()@ActivityThread.java 然後直到handleLaunchActivity()@ActivityThread.java 來建立Activity。

handleLaunchActivity()@ActivityThread.java
//利用performLaunchActivity()回傳一個activity
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
    r.createdConfig = new Configuration(mConfiguration);
    Bundle oldState = r.state;
    handleResumeActivity(r.token, false, r.isForward);
    ...
}
...

我們可以發現藉由performLaunchActivity()來建立一個activity之後呼叫handleResumeActivity() 先了解一下performLaunchActivity好了

performLaunchActivity()@ActivityThread.java
    ...
   
   //launch一個activity
   activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
    ...
   activity.mCalled = false;
  
   //在這裡呼叫activity裡頭的onCreate()
   mInstrumentation.callActivityOnCreate(activity, r.state);
   if (!activity.mCalled) {
       throw new SuperNotCalledException(
       "Activity " + r.intent.getComponent().toShortString() +
       " did not call through to super.onCreate()");
   }
   r.activity = activity;
   r.stopped = true;
   ...
return activity;
}

我們繼續看一下mInstrumentation.newActivity() 的部份。

newActivity()@Instrumentation.java
public Activity newActivity(Class clazz, Context context, 
            IBinder token, Application application, Intent intent, ActivityInfo info, 
            CharSequence title, Activity parent, String id,
            Object lastNonConfigurationInstance) throws InstantiationException, 
            IllegalAccessException {
        Activity activity = (Activity)clazz.newInstance();
        ActivityThread aThread = null;
        
        //設定一些activity的東西
        activity.attach(context, aThread, this, token, application, intent,
                info, title, parent, id,
                (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
                new Configuration());
        return activity;
    }

我們看一下activity.attach()  

attach()@Activity.java
...

//利用PolicyManager.makeNewWindow來產生一個window
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
那PolicyManager.makeNewWindow()是什麼呢?

makeNewWindow()@PolicyManager.java
    ...
    static {
        // Pull in the actual implementation of the policy at run-time
        try {
            //在run time的時候讀取
            //frameworks/base/policy/src/com/android/internal/policy/impl/Policy.java
            //一個PhonePolicy
            Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
            sPolicy = (IPolicy)policyClass.newInstance();
        } catch (ClassNotFoundException ex) {
            ...
        }
    }

    // The static methods to spawn new policy-specific objects
    public static Window makeNewWindow(Context context) {
        return sPolicy.makeNewWindow(context);
    }
如果我們去看Policy.java就可以發現真正的makeNewWindow了

makeNewWindow()@Policy.java
public Window makeNewWindow(Context context) {
        return new PhoneWindow(context);
}
回到performLaunchActivity(),他會去呼叫Activity裡面的onCreate()而在這裡面我們會利用setContentView()來設定我們的UI,接著看一下handleResumeActivity()

handleResumeActivity()@ActivityThread.java
     ...
if (r.window == null && !a.mFinished && willBeVisible) {
    r.window = r.activity.getWindow();
    
    //取得decorView
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    
    //取得WindowManager
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
    l.softInputMode |= forwardBit;
    if (a.mVisibleFromClient) {
        a.mWindowAdded = true;
        
        //將取得的 decor加入取得的WindowManager
        wm.addView(decor, l);
    }

    
} 
       ...

這裡有兩個地方可以看的。第一個這個decorView是什麼呢?第二個,這個WindowManager又是哪來的呢? 第一部份,r.window.getDecorView()的前面r.window是什麼呢?剛剛我們追過就是phoneWindow所以getDecorView()我們就直接看
PhoneWindow.getDecorView()

getDecorView()@PhoneWindow.java
  
@Override
    public final View getDecorView() {
        if (mDecor == null) {
            installDecor();
        }
        return mDecor;
    }
private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
        }
        if (mContentParent == null) {
            //generateLayout input是mDecor return 一個viewgroup
            mContentParent = generateLayout(mDecor);
      ...
}

protected ViewGroup generateLayout(DecorView decor) {
        ...
        //取得title 並加入decor中
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        
        //findViewById實際上是return getDecorView().findViewById(id);
        //他會去尋找{@link android.app.Activity#onCreate}所定義的xml
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        ...
        return contentParent;
}

因此我們可以發現,最剛開始windows manager裡面的view是一個PhoneWindow而PhoneWindow裡面是一個decorView裡面又包含了programmer定義的view 這是一個Decorator的design pattern 接著分析完了handleResumeActivity中的view, viewmanager之後我們繼續看 wm.addView(decor,l)

addView()@Window.java
public final void addView(View view, ViewGroup.LayoutParams params) {
            // Let this throw an exception on a bad params.
            WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
            CharSequence curTitle = wp.getTitle();
            
            ...
            super.addView(view, params);
        }
而LocalWindowManager 他是extends WindowManagerImpl.CompatModeWrapper, 因此我們去看一下WindowManagerImpl 的CompatModeWrapper才知道addView是寫些什麼

addView()@WindowManagerImpl.java
public void addView(View view, ViewGroup.LayoutParams params) {
        addView(view, params, null, false);
}

private void addView(View view, ViewGroup.LayoutParams params,
            CompatibilityInfoHolder cih, boolean nest) {
        
        ...
        //ViewRootImpl是我們接下來的重點
        ViewRootImpl root;
        View panelParentView = null;
        
        synchronized (this) {
            ...
            
            root = new ViewRootImpl(view.getContext());
            root.mAddNesting = 1;
            ...
            mViews[index] = view;
            mRoots[index] = root;
            mParams[index] = wparams;
        }
        // do this last because it fires off messages to start doing things
        //這裡的view是我們一開始addView(mDecor,l)中的mDecor
        root.setView(view, wparams, panelParentView);
    }
我們進入ViewRootImpl了解一下

ViewRootImpl.java
//ViewRootImpl繼承了Handler
public final class ViewRootImpl extends Handler implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    //這裡建立一個surface 的 object
    private final Surface mSurface = new Surface();
    //還有一個W 的object, W這個class 是繼承IWindow.stub
    final W mWindow;
    ....
    public ViewRootImpl(Context context) {
        ....
        //初始化對
        getWindowSession(context.getMainLooper());
        ....
        mWindow = new W(this);
        ....
    }
    ....
}
public static IWindowSession getWindowSession(Looper mainLooper) {
        synchronized (mStaticInit) {
            if (!mInitialized) {
                try {
                    InputMethodManager imm = InputMethodManager.getInstance(mainLooper);
                    //open session透過Binder與WindowManagerService做溝通做溝通
                    sWindowSession = Display.getWindowManager().openSession(
                            imm.getClient(), imm.getInputContext());
                    mInitialized = true;
                } catch (RemoteException e) {
                }
            }
            return sWindowSession;
        }
}
ok看完了ViewRootImpl的一些global variable及constructure之後我們回到剛剛的root.setView() setView()@ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
               ...

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();//實際上就是scheduleTraversals()
                ...
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    res = sWindowSession.add(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mAttachInfo.mContentInsets,
                            mInputChannel);
                }
                ...
    }
public void requestLayout() {
        checkThread();
        mLayoutRequested = true;
        
        scheduleTraversals();
}
public void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;

            //noinspection ConstantConditions
            if (ViewDebug.DEBUG_LATENCY && mLastTraversalFinishedTimeNanos != 0) {
                final long now = System.nanoTime();
                Log.d(TAG, "Latency: Scheduled traversal, it has been "
                        + ((now - mLastTraversalFinishedTimeNanos) * 0.000001f)
                        + "ms since the last traversal finished.");
            }

            sendEmptyMessage(DO_TRAVERSAL);
        }
}
public void handleMessage(Message msg) {
        switch (msg.what) {
        ...
        case DO_TRAVERSAL:
            ...

            performTraversals();

            ...
}
private void performTraversals() {
   .... 
   //取得一個surface
   relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
   ....
   //從surface裡面取得一個canvas然後畫在上面再把他show到螢幕
   draw(fullRedrawNeeded);
   ....
}
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {

        ....
       //透過IWindowSession與WindowManagerService溝通取得一個surface 
       int relayoutResult = sWindowSession.relayout(
                mWindow, mSeq, params,
                (int) (mView.getMeasuredWidth() * appScale + 0.5f),
                (int) (mView.getMeasuredHeight() * appScale + 0.5f),
                viewVisibility, insetsPending ? WindowManagerImpl.RELAYOUT_INSETS_PENDING : 0,
                mWinFrame, mPendingContentInsets, mPendingVisibleInsets,
                mPendingConfiguration, mSurface);
        //Log.d(TAG, "<<<<<< BACK FROM relayout");
        
        ....
        return relayoutResult;
    }

private void draw(boolean fullRedrawNeeded) {
        //mSurface是剛剛ViewRootImpl的global variable
        Surface surface = mSurface;
        ...
        
        if (!dirty.isEmpty() || mIsAnimating) {
            Canvas canvas;
            try {
                int left = dirty.left;
                int top = dirty.top;
                int right = dirty.right;
                int bottom = dirty.bottom;

                ....
                //lock一塊canvas
                canvas = surface.lockCanvas(dirty);

                ....
                    try {
                        ....
                        //把東西畫到canvas上
                        mView.draw(canvas);
                    } finally {
                        ....
                    }

                    ....
                }

            } finally {
                //把canvas show到螢幕上
                surface.unlockCanvasAndPost(canvas);
            }
        }

        ....
        if (animating) {
            mFullRedrawNeeded = true;
            scheduleTraversals();
        }
    }
總結一下,在這一篇我們說了
  1. 建立一個Activity之後會有一個PhoneWindow 這個PhoneWindow裡面包含了DecorView他是一個framelayout 我們的app所畫得ui會包含在Decorview裡頭 
  2. 建立一個Activity之後也會產生一個LocalWindowManager主要實做在WindowManagerImpl而他包含了ViewRootImpl
  3. ViewRootImpl包含了一個view指向DecorView及透過Binder從WindowServiceManager取得的surface
  4. 要畫出ui首先透過mSurface lock一塊canvas 然後透過mView.draw畫在上面 然後unlock 之後把他show在螢幕上

此篇是依照鄧凡平的Android系統原理深入解析 去trace 而成

2012年4月6日 星期五

Android SurfaceFlinger Part 1 - 啟動流程

SurfaceFlinger啟動方法有兩種

  • 方法一: 隨著zygote啟動(Default)

       啟動的流程是 main@SystemServer.java ->system_init@com_android_server_SystemServer.cpp
        ->system_init@system_init.cpp
       在system_init的function裡寫著

    property_get("system_init.startsurfaceflinger", propBuf, "1");
        if (strcmp(propBuf, "1") == 0) {
        // Start the SurfaceFlinger
        SurfaceFlinger::instantiate();
    }
     ...
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

        因此,假設系統中不存在"system_init.startsensorservice"就會執行SurfaceFlinger::instantiate();定義    在BinderService.h 最終執行
 sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
        將surfaceflinger加入系統的service.


  • 方法二:直接執行surfaceflinger

       我們可以在例如init.rc裡面直接去啟動surfaceflinger,程式碼位置是main_surfaceflinger.cpp 程式碼的內容是

  int main(int argc, char** argv) {
      SurfaceFlinger::publishAndJoinThreadPool();
      return 0;
  }
     SurfaceFlinger定義在SurfaceFlinger.h,而他是繼承BinderService 定義在BinderService.h 因此,我們可以在  BinderService.h看到 publishAndJoinThreadPool()的實做
    sp proc(ProcessState::self());
    sp sm(defaultServiceManager());
    sm->addService(String16(SERVICE::getServiceName()), new SERVICE());
    ProcessState::self()->startThreadPool();
    IPCThreadState::self()->joinThreadPool();

兩種方法都是利用
 sm->addService(String16(SERVICE::getServiceName()), new SERVICE());   
將surfaceflinger加入service並利用
ProcessState::self()->startThreadPool();
IPCThreadState::self()->joinThreadPool();
進入surfaceflinger裡的
SurfaceFlinger::readyToRun()
定義在Thread::_threadLoop@Threads.cpp

...
self->mStatus = self->readyToRun();
...
readyToRun()會做幾件事:

  • 建立GraphicPlane
  • GraphicPlane& plane(graphicPlane(dpy));
    DisplayHardware* const hw = new DisplayHardware(this, dpy);
    plane.setDisplayHardware(hw);

    在建立DisplayHardware的物件時init()@DisplayHareware,會new 一個FramebufferNativeWindow 的object FramebufferNativeWindow()@FramebufferNativeWindow 然後分別建立fbdev framebuffer_open()@gralloc.h及grdev gralloc_open()@gralloc.h 最後都會透過gralloc_device_open()@gralloc.cpp來建立,
    如果是framebuffer的話,會呼叫fb_device_open()@framebuffer.cpp 在裡面要注意的是他透過mapFrameBuffer()->mapFrameBufferLocked()嘗試開啟
    char const * const device_template[] = {
        "/dev/graphics/fb%u",
        "/dev/fb%u",
        0 };
    
    與kernel做溝通. 如果是graphic的話就要看平台實做了。


  • 初始化EGL
    • 取得Display資訊
    • EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
      eglInitialize(display, NULL, NULL);
      eglGetConfigs(display, NULL, 0, &numConfigs);
      err = selectConfigForPixelFormat(display, attribs, format, &config);
      

    • 建立main surface by call eglCreateWindowSurface()
    • surface = eglCreateWindowSurface(display, config, mNativeWindow.get(), NULL);
      if (mFlags & PARTIAL_UPDATES) {
          // if we have partial updates, we definitely don't need to
          // preserve the backbuffer, which may be costly.
          eglSurfaceAttrib(display, surface,
                      EGL_SWAP_BEHAVIOR, EGL_BUFFER_DESTROYED);
      }
      
    • 取得LCD 的density
    •     if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) {
              if (property_get("ro.sf.lcd_density", property, NULL) <= 0) {
                  LOGW("ro.sf.lcd_density not defined, using 160 dpi by default.");
                  strcpy(property, "160");
              }
          } else {
              /* for the emulator case, reset the dpi values too */
              mDpiX = mDpiY = atoi(property);
          }
          mDensity = atoi(property) * (1.0f/160.0f);
      
    • 建立的Context
    • context = eglCreateContext(display, config, NULL, contextAttributes);
      
      context存放著當前的 顏色,texture 等等的資訊
    • 暫時 unBind context
    • eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);

  • 初始化hw composer
  • mHwc = new HWComposer(mFlinger);
    if (mHwc->initCheck() == NO_ERROR) {
        mHwc->setFrameBuffer(mDisplay, mSurface);
    }
    
  • Bind context
  • 最後回到readyToRun()透過hw.makeCurrent();來bind context接著就可以開始畫圖了
  • 播放動畫
  • property_set("ctl.start", "bootanim");
在readyToRun()跑完之後,就會進入SurfaceFlinger::threadLoop()在下一篇會討論

2012年3月23日 星期五

Android Power Management

Overview

Android 的系統狀態可以分成以下五種(暫時忽略idle):


其中代表的意義如下

  • No Power        :  在沒有任何電源的時候( 包含battery/AC)
  • Off                   :  接上電源的時候(還沒按開機, G2)
  • Active              :  power on 之後( S0)
  • Early Suspend  : 當使用者過一段時間沒有動作後或是按下電源鍵,一般就是螢幕暗掉的時候
  • Suspend           : 當系統進入early suspend且沒有wake lock之後( S3 or S4)
以下將分開說明wakelock, earlysuspend/late resume 及suspend


wakelock

Android 的wakelock用途是要避免系統進入 low power mode ex. suspend or earlysuspend。舉個例來說,當我們在播電影的時候,使用者可能只會用"看"而不會去操作,為了避免系統的螢幕被關掉,我們就可以建立一個wakelock。

wakelock in Framework

Android 定義了4種互斥(四選一)的lock,下表分別列出其對於CPU, screen及keyboard的影響如下 :

Flag Value
CPU
Screen
Keyboard
On*
Off
Off
On
Dim
Off
On
Bright
Off
On
Bright
Bright

如果是partial_wake_lock, 系統的CPU永遠會是run的狀態(包含按下power button),其他的lock如果使用者按下power button依然會進入earlysuspend。另外,對於screen還有兩個額外的lock可以加入(如果與partial_wake_lock結合則不會有任何效果):


Flag Value
Description
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.
If this flag is set, the user activity timer will be reset when the WakeLock is released, causing the illumination to remain on a bit longer. This can be used to reduce flicker if you are cycling between wake lock conditions.


在上層要使用wakelock可以參考:

PowerManager pm =(PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.new WakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK| PowerManager.ON_AFTER_RELEASE,TAG);
wl.acquire();  
// ... 
wl.release();



Earlysuspen/Lateresume

Framework

要進入earlysuspend的情況就是在Android裡
$ echo mem >/sys/power/state 
or
$ echo standby >/sys/power/state 
從earlysuspend回來的方法(lateresume)即是
$ echo on>/sys/power/state

以上圖來說,當使用者沒有動作時,Android的上層就會倒數,到0之後,就會由圖所示的call path一路透過call JNI 鏈結Android_os_Power.cpp再到Power.c最後直接下command給/sys/power/state 進入earlysuspend。此時,只會有panel, touch等等裝置會關閉(depends on kernel 實作)。

Kernel


在進入kernel後,依照上圖的sequence diagram跑,最後在early_suspend_work裡面會透過list_for_each_entry()去call 有註冊進earlysuspend list裡的driver,執行對應的earlysuspend code。


Suspend

wakelock in Kernel

上面介紹的 wakelock 是framework的部分,這裡要提到的wakelock是kernel的部分。當上層透過command新增一個wakelock給kernel的時候eg.
$ echo wakelock_name>/sys/power/wakelock
對於kernel來說,這是一個WAKE_LOCK_SUSPEND的wakelock,另外一種是WAKE_LOCK_IDLE在此忽略。

當新增了一個wakelock,他的流程可以參考上圖。Kernel的wakelock在以下的情況會去檢查是否仍然有wakelock存在
  • 解除wakelock ( $ echo wakelock_name >/sys/power/wake_unlock)
  • wakelock timer expired ( kernel裡面有一個timer會固定時間檢查是否有wakelock存在)

檢查的function是wake_has_wake_lock_locked(),當發現沒有 wakelock的時候就會呼叫suspend_work() 開始執行suspend的工作。相對應的suspend應該都會實作在arch/arm/mach-xxx裏頭。

相關的程式碼

  • frameworks/base/core/java/android/os
    • PowerManager.java
    • Power.java
  • frameworks/base/services/java/com/android/server
    • PowerManagerService.java
  • hardware/libhardware_legacy/power
    • power.c
  • frameworks/base/core/jni
    • android_os_Power.cpp
  • kernel/kernel/power
    • main.c( earlysuspend entry point)
    • wakelock.c
    • userwakelock.c
    • suspend.c
    • earlysuspend.c

Reference

Android Power Management, http://blog.csdn.net/hzdysymbol/article/details/4004791s3c2440電源管理主要問題及其解決方法, http://www.linuxforum.net/forum/printthread.php?Cat=&Board=embedded&main=725407&type=threadAndroid內核驅動——電源管理, http://www.docin.com/p-115475680.html#documentinfoa n d r o i d 休眠與喚醒, http://wenku.baidu.com/view/a4e4ab8483d049649b6658f3.htmlLinuxAndroid休眠喚醒對比(二), http://hi.baidu.com/ch_ff/blog/item/aa660bd8d4f6563811df9b96.htmlLinux Kernel and Android 休眠與喚醒, http://www.thinksrc.com/2010/04/18/suspend-cn.htmlAndroid Power Management, partial wake lock parts, http://tech-sjh.blogspot.com/2011/08/android-power-management-wake-lock.htmlLinuxsuspend機制的設計原理, http://www.4ucode.com/Study/Topic/1282150Tegra board support package for Android

Android LED Implementation


  1. 在config裡勾選
    CONFIG_NEW_LEDS=y
    CONFIG_LEDS_CLASS=y
    CONFIG_LEDS_GPIO=y
    CONFIG_LEDS_TRIGGERS=y
    CONFIG_LEDS_TRIGGER_TIMER=y 

  2. 在board-*.c 裡面建立platform_device 
    static struct gpio_led gpio_leds[] = {
     {
     .name = "WHITE_LED",//should change to your own
     .gpio = _GPIO_1,    //should change to your own
     .active_low = 1,
     .default_trigger = "timer", 
     },
    };
    
    static struct gpio_led_platform_data gpio_led_info = {
     .leds = gpio_leds,
     .num_leds = ARRAY_SIZE(gpio_leds),
    };
    
    static struct platform_device leds_gpio = {
     .name = "leds-gpio",
     .id = -1,
     .dev = {
     .platform_data = &gpio_led_info,
     },
    };
    //記得要在類似init加入-->
    platform_device_register(&leds_gpio);
    

  3. 實做platform_driver
    基本上依我的板子為例,platform_driver的實做已經在driver/leds/leds-gpio.c裡面做好了 不需要更改

  4. 修改LED HAL
    以我的版為例,修改的是libleds.c
    在
    static int open_lights(const struct hw_module_t *module, char const *name,
             struct hw_device_t **device)
    裡面新增了
    }else if(0 == strcmp(LIGHT_ID_BATTERY, name)){
        set_light = set_light_battery;
    }
    
    
    然後因為我的led是gpio led, 只支援on/off ,沒有brightness level, Android定義了不同的level會有不同的顏色或閃爍
    因此我的實做為
    
    
    static int set_light_battery(struct light_device_t *dev,
              struct light_state_t const *state)
    {
     int err = 0;
     int flashmode=state->flashMode;
     
     pthread_mutex_lock(&g_lock);
            
            //set flash
     if(flashmode==1){
      err = write_str("/sys/class/leds/PMU_LED/trigger","timer");
      err = write_int("/sys/class/leds/PMU_LED/delay_on",250);
      err = write_int("/sys/class/leds/PMU_LED/delay_off",150);
     
            }else if(state->color ==0xffffff00||state->color ==0xffff0000||state->color ==-256){
      err = write_str("/sys/class/leds/PMU_LED/trigger","none");
      err = write_int("/sys/class/leds/PMU_LED/brightness",0);
     }else if(state->color ==0xff00ff00||state->color ==0x0){
      err = write_str("/sys/class/leds/PMU_LED/trigger","none");
      err = write_int("/sys/class/leds/PMU_LED/brightness",255);
     }
     pthread_mutex_unlock(&g_lock);
    
     return err;
    }