1. 程式人生 > >系統工程師之系統升級研究

系統工程師之系統升級研究

前一陣子,一直忙專案並負責了系統升級這一塊。簡單總結和分析系統升級模組的處理。

首先明確系統升級UI以及大概思路:

1.系統升級單獨作為一個apk進行編寫。啟動系統升級apk可以用:

Intent intent = new Intent ();

intent.setClassName(arg1,arg2);    //arg1為要啟動的apk的包名,arg2為要啟動apk的具體activity名

startActivity(intent);

2.系統升級apk顯示的方式。這裡我設定成dialog樣式。

說到這裡就解釋一下系統自帶的一些theme

•android:theme=”@android:style/Theme.Dialog”  將一個Activity顯示為對話方塊模式
•android:theme=”@android:style/Theme.NoTitleBar”  不顯示應用程式標題欄
•android:theme=”@android:style/Theme.NoTitleBar.Fullscreen”  不顯示應用程式標題欄,並全屏
•android:theme=”@android:style/Theme.Light”  背景為白色
•android:theme=”@android:style/Theme.Light.NoTitleBar”  白色背景並無標題欄
•android:theme=”@android:style/Theme.Light.NoTitleBar.Fullscreen”  白色背景,無標題欄,全屏
•android:theme=”@android:style/Theme.Black”  背景黑色
•android:theme=”@android:style/Theme.Black.NoTitleBar”  黑色背景並無標題欄
•android:theme=”@android:style/Theme.Black.NoTitleBar.Fullscreen”   黑色背景,無標題欄,全屏
•android:theme=”@android:style/Theme.Wallpaper”  用系統桌面為應用程式背景
•android:theme=”@android:style/Theme.Wallpaper.NoTitleBar”  用系統桌面為應用程式背景,且無標題欄
•android:theme=”@android:style/Theme.Wallpaper.NoTitleBar.Fullscreen”  用系統桌面為應用程式背景,無標題欄,全屏
•android:theme=”@android:style/Translucent”半透明效果
•android:theme=”@android:style/Theme.Translucent.NoTitleBar”  半透明並無標題欄
•android:theme=”@android:style/Theme.Translucent.NoTitleBar.Fullscreen”  半透明效果,無標題欄

3.考慮升級的時間點,這裡升級檢測的時刻,我選擇wifi開啟的時候.

所以我需要註冊一個廣播,來接收wifi 的改變。

<receiver android:name=”com.xiaoshou.systemupdate.WifiReceiver” >
<intent-filter>
<action android:name=”android.net.wifi.RSSI_CHANGED” />
<action android:name=”android.net.wifi.STATE_CHANGE” />
<action android:name=”android.net.wifi.WIFI_STATE_CHANGED” />
</intent-filter>
</receiver>

wifiReceiver是負責處理當wifi 狀態發生改變工作。我處理了三種狀態也就是對應的上面三個action.

在onreceiver 中做相應的處理。
if (intent.getAction().equals(WifiManager.RSSI_CHANGED_ACTION)) { //訊號強度變化後的處理
} else if (intent.getAction().equals(
WifiManager.NETWORK_STATE_CHANGED_ACTION)) {// wifi連線上與否
System.out.println(“網路狀態改變”);
NetworkInfo info = intent
.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
if (info.getState().equals(NetworkInfo.State.DISCONNECTED)) {
System.out.println(“wifi網路連線斷開”);
} else if (info.getState().equals(NetworkInfo.State.CONNECTED)) {
Log.i(TAG, “wifi 連線成功”);

//這裡做檢測新版本,並將檢測到的新版本資訊插入到資料庫,如果希望別的應用能訪問到,需要用contentprovider進行封裝。

//contentprovider只儲存最新版本資訊,這個由伺服器中返回。這裡單獨寫個工具類檢測做檢測更新的響應的操作。CheckUpdate

}

} else if (intent.getAction().equals(
WifiManager.WIFI_STATE_CHANGED_ACTION)) {// wifi開啟與否
int wifistate = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_DISABLED);
if (wifistate == WifiManager.WIFI_STATE_DISABLED) {
System.out.println(“系統關閉wifi”);
} else if (wifistate == WifiManager.WIFI_STATE_ENABLED) {
System.out.println(“系統開啟wifi”);
}
}
checkupdate 類

/**
     * check the update Inner
     *
     * @param strSN
     * @param strVer
     * @param checkUrl
     * @return
     */
    private static int checkUpdateInner(String strSN, String strVer, String checkUrl)
    {
        String sn = "sn=" + strSN;
        String version = "version=" + strVer;
        String modelClient = SystemFacade.getModelClient();
 
        HttpPost httpRequest = new HttpPost(checkUrl);
        HttpClient httpClient = AndroidHttpClient.newInstance("CheckUpdate");

        try
        {            
            httpRequest.setEntity(new ByteArrayEntity(buildRequest(strSN, strVer, modelClient).toByteArray()));

            HttpResponse httpResponse = httpClient.execute(httpRequest);
//            httpResponse.getEntity().getContentLength();
            
            SystemFacade.LOGD("--dela--", "--CheckUpdate.java--checkUpdateInner()--content length == " + httpResponse.getEntity().getContentLength());
            
            SystemFacade.LOGD(TAG,
                    "Flash test : ++++ httpResponse.getStatusLine().getStatusCode() = "
                            + httpResponse.getStatusLine().getStatusCode());
            
            int responseCode = httpResponse.getStatusLine().getStatusCode();

            if (isStatusSuccess(responseCode))
            {
                SystemFacade.LOGD(TAG, "responseCode status success");

                UpdateMsg msg = buildResponseMsg(httpResponse);

                SystemFacade.LOGD("--dela--", "--responseCode status success--00--");
                
                if (msg != null)
                {
                    SystemFacade.LOGD(TAG, msg.toString());
                    if (msg.isEmptyMsg())
                    {
                        SystemFacade.LOGD(TAG, "no update");
                        returnCode = CHECK_ALREADY_NEWEST;
                    }
                    else
                    {
                        Log.d(
                                TAG,
                                "!-------type----------!" + msg.getType()
                                        + "!-------id----------!" + msg.getId()
                                        + "!--------des---------!" + msg.getDes()
                                        + "!-------isnecessary----------!"
                                        + msg.getIsNecessary()
                                        + "!-------path----------!" + msg.getPath()
                                        + "!-------version----------!"
                                        + msg.getVersion()
                                        + "!-------mark----------!"
                                        + msg.getCheckMark()
                                        + "!--------total size---------!"
                                        + msg.getTotalSize()
                                        + "!------save path-------!"
                                        + msg.getSavePath());
                        UpdatePackageManager manager = UpdatePackageManager.getInstance(UserApplication.getApplication());
                        manager.insertUpdateMsg(msg);
                        returnCode = CHECK_HAS_UPDATE;
                    }
                }
                else
                {
                    SystemFacade.LOGD(TAG, "---------------" + "no update");
                    returnCode = CHECK_ALREADY_NEWEST;
                }
            }
            else
            {
                SystemFacade.LOGD(TAG, "not Status Success " + httpResponse.getStatusLine().getStatusCode());
                returnCode = CHECK_ERROR;
            }

        } catch (Exception e) {
            // return CHECK_HAS_UPDATE;
            SystemFacade.LOGD(TAG, " isUpdateEnable exception");
            e.printStackTrace();
            returnCode = CHECK_ERROR;
        } finally {
            if (httpClient instanceof AndroidHttpClient)
                ((AndroidHttpClient) httpClient).close();
            else
                httpClient.getConnectionManager().shutdown();
        }

        return returnCode;
    }

這裡值得注意的是,經過測試,客戶端傳送當前裝置的ID和當前裝置的版本號,以及伺服器請求地址。伺服器會判斷改裝置是否是最新版本,如果是最新版本,返回一個值都是空的UpdateMsg,UpdateMsg類的定義如下:

package com.xiaoshou.systemupdate.msg.model;

import com.xiaoshou.systemupdate.msg.model.ProtocolMsg.ClientVersion;

public class UpdateMsg {
	
	//update package id in database
	private long id;
	//update package version
	private String version;
	//update package whole version type
	private String type;
	//update package descripe
	private String des;
	//update package request path
	private String path;
	//update package save path
	private String savePath;
	//update package priority
	private String isNecessary;
	//update package check mark
	private String checkMark = "";
	//update package total size
	private long totalSize;
	//update package current size
	private int currentSize;
	
	
	public UpdateMsg(ClientVersion version) {

		this.version = version.getVersionCode();
		this.type = version.getType();
		this.des = version.getDes();
		this.path = version.getPath();
		this.isNecessary = version.getIsNecessary();
		this.checkMark = version.getId();
		try {
//			this.totalSize = Integer.parseInt(version.getFileSize());
			this.totalSize = Long.parseLong(version.getFileSize());
		} catch (NumberFormatException e) {
			this.totalSize = 0;
		}
	}
	
	public UpdateMsg(){
		
	}
	
	public boolean isEmptyMsg(){
		return (this.path == null || "".equals(this.path));
	}
	
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public String getVersion() {
		return version;
	}
	public void setVersion(String version) {
		this.version = version;
	}
	public String getType() {
		return type;
	}
	public void setType(String type) {
		this.type = type;
	}
	public String getDes() {
		return des;
	}
	public void setDes(String des) {
		this.des = des;
	}
	public String getPath() {
		return path;
	}
	public void setPath(String path) {
		this.path = path;
	}
	public String getSavePath() {
		return savePath;
	}

	public void setSavePath(String savePath) {
		this.savePath = savePath;
	}

	public long getTotalSize() {
		return totalSize;
	}

	public void setTotalSize(long totalSize) {
		this.totalSize = totalSize;
	}

	public int getCurrentSize() {
		return currentSize;
	}

	public void setCurrentSize(int currentSize) {
		this.currentSize = currentSize;
	}
	
	public String getIsNecessary() {
		return isNecessary;
	}
	public void setIsNecessary(String isNecessary) {
		this.isNecessary = isNecessary;
	}
	public String getCheckMark() {
		return checkMark;
	}
	public void setCheckMark(String checkMark) {
		this.checkMark = checkMark;
	}

	@Override
	public String toString() {
		return "UpdateMsg [id=" + id + ", version=" + version + ", type=" + type + ", des=" + des
				+ ", path=" + path + ", savePath=" + savePath + ", isNecessary=" + isNecessary
				+ ", checkMark=" + checkMark + ", totalSize=" + totalSize + ", currentSize="
				+ currentSize + "]";
	}
	
	
}

回到檢測跟新checkUpdateInner方法,前面已經分析通過傳送裝置ID 和裝置version 以及請求伺服器的地址,伺服器會返回相應的新版本資訊對應UpdateMsg各個屬性。如果沒有最新版本會返回一個數據為空的UpdateMsg物件。

如果沒有新版本,這裡不做處理,結束檢測。如果有新版本,將新版本的資訊插入到Contentprovider中,ContentProvider中就存放了伺服器中的最新版本資訊。到此檢測更新的操作基本完成。小結一下:

wifi開啟檢測伺服器上是否有新版本,有新版本插入到Contentprovider中,ContentProvider的實現下篇做介紹。現在只需要知道Contentprovider中是存有最新版本資訊的。

至此完成了初步檢測更新,並將檢測後的結果儲存到ContentProvider中。

接下來我們跟據已經獲得的伺服器版本資訊,做邏輯處理。我以我的案例來說:設定中版本號這一欄如果有新版本需要顯示有個圖示標識。這裡的實現方案有兩種:

1.在檢視建立之前,通過網路請求判斷。

2.前面我們已經將新的版本資訊存入了Contentprovider中,更據contentProvicer中的資料進行判斷顯示。

從時間效率上我選擇第二種。

首先獲取當前系統資訊(版本號)

private static final String DEV_VERSION = "ro.boeye.version";   //這個系統變數儲存了在編譯完成後會的系統版本資訊
CharSequence summary = SystemProperties.get(DEV_VERSION, null);

接下來我們只需要將當前的版本號與contentprovider中的版本資訊作大小比較,判斷當前版本是否未最新版本。如果當前版本大於或等於contentprovider中的資訊的版本號,則沒有最新版本。

	public boolean isLatestVersion() {
		String selection = UpdateColumn.UPDATE_VERSION + " > ?";
		Cursor cursor = cr.query(UpdateColumn.CONTENT_URI,
		new String[] { UpdateColumn.UPDATE_ID }, selection,
		new String[] { version }, UpdateColumn.DEFAULT_SORT);
		int count = 0;
		if (cursor != null) {
			count = cursor.getCount();
			cursor.close();
		}
		if (count <= 0) {
			return true;
		} else {
			return false;
		}
	}

其他的邏輯就跟據自己需要進行編寫了。這裡基本完成了當前版本是否是最新版本的驗證工作。

接 下來我們看下載,有了最新版本,我們就要進行下載的邏輯編寫了。前面已經知道如果有新版本,我們會將新版本資訊儲存到contentprovider中。 對新資訊可以從contentprovider中獲取。其他簡單邏輯我這裡就不詳細講了,直接進行下載邏輯。關於下載對於使用者的幾個介面:

1.取消下載。

2.下載。

3.後臺下載。

首先明確我們下載採用的是系統提供的下載框架DownloadManager。

DownloadManager是Android 2.3引入的,基於http協議,用於處理長時間下載。下面是官方的對DownloadManaager的解釋

**
 * The download manager is a system service that handles long-running HTTP downloads. Clients may
 * request that a URI be downloaded to a particular destination file. The download manager will
 * conduct the download in the background, taking care of HTTP interactions and retrying downloads
 * after failures or across connectivity changes and system reboots.
 *
 * Instances of this class should be obtained through
 * {@link android.content.Context#getSystemService(String)} by passing
 * {@link android.content.Context#DOWNLOAD_SERVICE}.
 *
 * Apps that request downloads through this API should register a broadcast receiver for
 * {@link #ACTION_NOTIFICATION_CLICKED} to appropriately handle when the user clicks on a running
 * download in a notification or from the downloads UI.
 *
 * Note that the application must have the {@link android.Manifest.permission#INTERNET}
 * permission to use this class.
 */
public class DownloadManager {

我就挑重點說明一下DownLoadManger的使用。

1.第一步要使用DownloadManager需要在app中註冊許可權。

<permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />

2.獲取DownloadManager服務

(DownloadManager)getSystemService(DOWNLOAD_SERVICE);

3.啟動下載服務

寫法一:

     ContentValues values = new ContentValues();
      values.put(Downloads.URI, url);//指定下載地址
      values.put(Downloads.COOKIE_DATA,cookie);//如果下載Server需要cookie,設定cookie
      values.put(Downloads.VISIBILITY,Downloads.VISIBILITY_HIDDEN);//設定下載提示是否在螢幕頂部顯示 
      values.put(Downloads.NOTIFICATION_PACKAGE,getPackageName());//設定下載完成之後回撥的包名 
      values.put(Downloads.NOTIFICATION_CLASS,DownloadCompleteReceiver.class.getName());//設定下載完成之後負責接收的Receiver,這個類要繼承BroadcastReceiver     
      values.put(Downloads.DESTINATION,save_path);//設定下載到的路徑,這個需要在Receiver裡自行處理
      values.put(Downloads.TITLE,title);//設定下載任務的名稱
       this.getContentResolver().insert(Downloads.CONTENT_URI,values);//將其插入到DownloadManager的資料庫中,資料庫會觸發修改事件,啟動下載任務
  如何為DownloadManager設定代理,比如Wap
       values.put(Downloads.PROXY_HOST,"10.0.0.172");
       values.put(Downloads.PROXY_PORT,"80");


寫法二:

	public void startDownload()
	{
//		if(!prefs.contains(DL_ID))
		{
//			String url = "http://42.121.0.14/cms/firmware/Icarus-E653/2014081420/update.zip";
			String url = mUpdateMsg.getPath();
			Uri resource = Uri.parse(url);
			DownloadManager.Request request = new DownloadManager.Request(resource);
			request.setAllowedNetworkTypes(Request.NETWORK_MOBILE | Request.NETWORK_WIFI);
			request.setAllowedOverRoaming(false);
			
			SystemFacade.LOGD("--dela--", "--BrightnessDialogActivity.java--startDownload()--11--");
			
			MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
			String mimeString = mimeTypeMap.getMimeTypeFromExtension(MimeTypeMap.getFileExtensionFromUrl(url));
			request.setMimeType(mimeString);
			
			request.setShowRunningNotification(false);
			request.setVisibleInDownloadsUi(false);
			request.setNotificationVisibility(Request.VISIBILITY_HIDDEN);
			
			File sdCard = Environment.getExternalStorageDirectory();
			String folder = sdCard.getAbsolutePath() + mUpdateManager.DOWNLOADS_DIR;
			File dir = new File(folder);
			if(!dir.exists())
			{
				if(dir.mkdirs())
				{
					SystemFacade.LOGD("--dela--", "-------create----dir ======= "+ folder);
				}
			}

			
			SystemFacade.LOGD("--dela--", "--BrightnessDialogActivity.java--startDownload()--22--");
			File downloadFile = new File(mUpdateManager.DOWNLOADS_DIR+mUpdateMsg.getVersion());
			if(downloadFile.exists())
				downloadFile.delete();
			
			
			request.setDestinationInExternalPublicDir(mUpdateManager.DOWNLOADS_DIR, mUpdateMsg.getVersion());
			request.setTitle("");
			long id = downloadManager.enqueue(request);
			
			prefs.edit().putLong(DL_ID, id).commit();
			
			this.lockScreenNotForSleep();
			
		}
//		else 
		{
//			queryDownloadStatus();
//			Log.i("--dela--", "--restart--()");
		}
		
//		registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
	}

此時下載任務資訊通過

long id = downloadManager.enqueue(request);

加 入到了DownloadManager的資料庫(contentProvider)中,通過監聽Contentprovider中的資料變化可以獲得當前 的下載狀態。在這裡返回的reference變數是系統為當前的下載請求分配的一個唯一的ID,我們可以通過這個ID重新獲得這個下載任務,進行一些自己 想要進行的操作或者查詢下載的狀態以及取消下載等等。下載任務會在呼叫enqueue()方法後開始,下載完成後系統會發送一個廣播 android.intent.action.DOWNLOAD_COMPLETE,我們可以通過註冊一個BroadcastReceiver來接收系統 發來的廣播訊息,並進行想要的邏輯處理.

回到對downloadManager的資料庫的監聽,通過對downloadManager原始碼你可以知道downloadManager對外提供的contentprovider的URI是

private static final Uri CONTENT_URI = Uri.parse("content://downloads/my_downloads");

這個可以通過追溯enqueue方法找到。接下來就是應用註冊監聽對應的資料庫的變化。在自己繼承的application類中註冊:

downloadManager = (DownloadManager)getSystemService(DOWNLOAD_SERVICE);    //獲取downloadManager的服務物件
		prefs = PreferenceManager.getDefaultSharedPreferences(this); //為該應用建立自己的preference,用來儲存當前下載任務的ID
		downloadChangeObserver = new DownloadChangeObserver();      //建立一個監視contentprovicder物件,繼承contentObserver
		getContentResolver().registerContentObserver(CONTENT_URI, true, downloadChangeObserver);  //註冊監聽

說到這裡簡單講一下contentobserver- 內容觀察者,目的是觀察(捕捉)特定Uri引起的資料庫的變化,繼而做一些相應的處理,它類似於資料庫技術中的觸發器(Trigger),當 ContentObserver所觀察的Uri發生變化時,便會觸發它。觸發器分為表觸發器、行觸發器,相應地ContentObserver也分為“表 “ContentObserver、“行”ContentObserver,當然這是與它所監聽的Uri MIME Type有關的。 熟悉Content Provider(內容提供者)的應該知道,我們可以通過UriMatcher類註冊不同型別的Uri,我們可以通過這些不同的,Uri來查詢不同的結 果。根據Uri返回的結果,Uri Type可以分為:返回多條資料的Uri、返回單條資料的Uri。會觸發它。觸發器分為表觸發器、行觸發器,相應地ContentObserver也分為 “表“ContentObserver、“行”ContentObserver,當然這是與它所監聽的Uri MIME Type有關的。下面簡單介紹使用的流程:

1、    建立我們特定的ContentObserver派生類,必須過載父類構造方法,必須過載onChange()方法去處理回撥後的功能實現
2、    利用context.getContentResolover()獲得ContentResolove物件,接著呼叫registerContentObserver()方法去註冊內容觀察者
3、    由於ContentObserver的生命週期不同步於Activity和Service等,因此,在不需要時,需要手動的呼叫
unregisterContentObserver()去取消註冊。

下面看看我的內容觀察者做的事項,獲取當前下載進度並通知UI執行緒更新:

class DownloadChangeObserver extends ContentObserver
	{
		public DownloadChangeObserver()
		{
			super(null);
		}
		
		@Override
		public void onChange(boolean selfChange)
		{
			//to do
			int[] status = new int[] {-1, -1, 0};
			DownloadManager.Query query = new DownloadManager.Query().setFilterById(prefs.getLong(DL_ID, 0));
			Cursor cr = null;
			try {
				cr = downloadManager.query(query);
				if(null != cr && cr.moveToFirst())
				{
					status[0] = cr.getInt(cr.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
					status[1] = cr.getInt(cr.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));
					status[2] = cr.getInt(cr.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
				}
			} catch (Exception e) {
				// TODO: handle exception
			}
			finally
			{
				if(null != cr)
				{
					cr.close();
				}
			}
			
			switch (status[2]) {
			case DownloadManager.STATUS_PAUSED:
				
				SystemFacade.LOGD("--dela--", "--download manager status pause--");
				
				break;
				
			case DownloadManager.STATUS_PENDING:
				
				SystemFacade.LOGD("--dela--", "--download manager status pending--");
				
				break;
				
			case DownloadManager.STATUS_RUNNING:
				
				float f1 = Float.parseFloat(String.valueOf(status[0]));
				float f2 = Float.parseFloat(String.valueOf(status[1]));
				BigDecimal b = new BigDecimal(f1/f2); 
				float f3 = b.setScale(2, BigDecimal.ROUND_HALF_UP).floatValue(); 
				int progress = (int)(f3*100);
				Intent intent = new Intent("android.rockchip.update.progress");  
				intent.putExtra("progress", progress);
				sendBroadcast(intent);
				lockScreenNotForSleep();
				
				SystemFacade.LOGD("--dela--", "--download manager status running--progress == " + progress + "--");
				
				break;

			case DownloadManager.STATUS_SUCCESSFUL:
				
				SystemFacade.LOGD("--dela--", "--download manager status success--");
				
				break;
				
			case DownloadManager.STATUS_FAILED:
				
				SystemFacade.LOGD("--dela--", "--download manager status failed--");
				
				break;
				
			default:
				break;
			}
			
			SystemFacade.LOGD("--dela--", "--BrightnessDialogActivity.java--onChange()--down so far == " + status[0] 
											+ "--total size == " + status[1]
											+ "--status == " + status[2] + "--");
		}
	};

這裡主要講一下流程首先獲取到我們下載任務的對應的contentResolver物件,我們通過自己下載任務ID,前面儲存 在preference中,獲取到DownloadManager的query物件正如Google解釋一樣他是作為一個過濾 器,downloadManager可以通過呼叫query方法跟進前面獲取的query物件作為過濾條件來得到對應的任務的資訊查詢遊標。獲得遊標 cursor後就可以查詢到我們下載任務的資訊了。並在

DownloadManager.STATUS_RUNNING:

狀態時通過一定的邏輯計算得出進度比例,併發送廣播。在UI執行緒獲取到廣播,及廣播發來的資料就可以更新UI了。

回到下載完成系統傳送廣播:android.intent.action.DOWNLOAD_COMPLETE:使用者註冊接收到廣播的後做的主要操作放到了另一個app中updateService. 下篇介紹當完成下載之後的工作。到這裡基本就介紹完了下載。
二。取消下載。

我們只需要在DownlaodManager的下載佇列中移除掉我們自己請求任務ID。直接上程式碼

		long downloadId = prefs.getLong(DL_ID, -1);
		if(downloadId >= 0)
		downloadManager.remove(downloadId);

三。後臺下載。

明確一個事情就好了只要下載任務一旦加入到downloadManager的請求佇列,只要沒有移除任務,他就會一直儲存下載任務。所以後臺下載,只需要 將當前應用finish()   掉。下次啟動是downloadManager會跟進任務ID查詢到上次的下載點,繼續下載。


上篇介紹了從檢測到升級包的下載。這篇我簡單介紹從下載完成後到系統安裝重啟並開機的整個流程:

上篇說了downloadManager下載完成後會發送一個廣播android.intent.action.DOWNLOAD_COMPLETE;我們通過監聽這個廣播,將系統升級包的任務交給另一個Apk去完成RKUpdateService4.4。上程式碼:

			Timer timer = new Timer(true);
			timer.schedule(new TimerTask() {
				
				@Override
				public void run() {
					// TODO Auto-generated method stub
					try
					{
						UpdateMsg msg = UpdatePackageManager.getInstance(UserApplication.getApplication()).getNewestUpdatePackage(null);
						File saveFile = new File(msg.getSavePath());
						
						if(saveFile.length() == msg.getTotalSize())
						{
							if(checkPackgeMd5Code(msg))
							{
								UserApplication.getApplication().unregisterContentObserver();
								
								Intent intent1 = new Intent("android.rockchip.update.finish");
								
								String path = SystemFacade.getDefaultUpdatePackage(msg.getPath());
								
								File update = new File(path);
								if(update.exists())
									update.delete();
								
								if(!saveFile.renameTo(update))
								{						
									String cmd = "mv " + msg.getSavePath() + " " + path;
									Runtime.getRuntime().exec(cmd);
									SystemFacade.LOGD("--dela--", "--UserApplication.java--download finish--cmd == " + cmd + "--");
								}
								
								intent1.putExtra("updateImgPath", path);
								mContext.sendBroadcast(intent1);
							}
						}
					}
					catch (DownloadException e)
					{
						e.printStackTrace();
					}
					catch (Exception e)
					{
						e.printStackTrace();
					}
				}
			}, 1000);

上面的程式碼做了這麼幾件事情:

1.獲取最新版本的資訊,並確定檔案未損壞,下載完成了。

2.取消對download的contentprovider的內容監聽。

3.將下載的檔案//mnt/sdcard//downloads/Boyue-Ereader_V1.0.0-2015121613 複製到/mnt/sdcard/update.zip中。

4.傳送廣播,讓專門的APK來處理安裝事項。

說到這就得轉移到處理系統安裝的APK中。首先APK需要一個廣播接收者來接收上面傳送的廣播

android.rockchip.update.finish

接受到廣播後啟動一個service去處理

if(action.equals("android.rockchip.update.finish"))
        {
        	Bundle bundle = intent.getExtras();
        	String imgPath = bundle.getString("updateImgPath");
        	serviceIntent = new Intent("android.rockchip.update.service");  
        	//serviceIntent.setComponent(new ComponentName("android.rockchip.update.service", "android.rockchip.update.service.FirmwareUpdatingActivity"));  
        	//serviceIntent.putExtra("android.rockchip.update.extra.IMAGE_PATH", bundle.getString("updateImgPath"));
        	//serviceIntent.putExtra("android.rockchip.update.extra.IMAGE_VERSION", "");
        	//serviceIntent.putExtra("android.rockchip.update.extra.CURRENT_VERSION", "");
        	serviceIntent.putExtra("command", RKUpdateService.COMMAND_UPDATE_FINISH);
        	serviceIntent.putExtra("delay", 5000);
        	serviceIntent.putExtra("updateImgPath", imgPath);
//        	context.startActivity(serviceIntent);
        	context.startService(serviceIntent);
        }

在處理的service中的onStartCommand中獲取command的值

int command = intent.getIntExtra("command", COMMAND_NULL);
		int delayTime = 10;
		
		Message msg = new Message();
		msg.what = command;
		msg.arg1 = WorkHandler.NOT_NOTIFY_IF_NO_IMG;
		mWorkHandler.sendMessageDelayed(msg, delayTime);
		return Service.START_REDELIVER_INTENT;

workHandler跟據command的值進行處理:

 case COMMAND_UPDATE_FINISH:
                {
                	LOG("WorkHandler::handleMessage() : To perform 'COMMAND_UPDATE_FINISH'.");
					if(mWorkHandleLocked){
						LOG("WorkHandler::handleMessage() : locked !!!");
						return;
					}
					
                    if ( null != (searchResult = getValidFirmwareImageFile(IMAGE_FILE_DIRS) ) ) 
                    {
                        if ( 1 == searchResult.length ) 
                        { 
                            String path = searchResult[0];      
							String imageFileVersion = null;
							String currentVersion = null;
							
							//if it is rkimage, check the image
                            if(path.endsWith("img")){
								if(!checkRKimage(path)){
									LOG("WorkHandler::handleMessage() : not a valid rkimage !!");
									return;	
								}

								imageFileVersion = getImageVersion(path);

								LOG("WorkHandler::handleMessage() : Find a VALID image file : '" + path 
										+ "'. imageFileVersion is '" + imageFileVersion);
                             
								 currentVersion = getCurrentFirmwareVersion();
								 LOG("WorkHandler::handleMessage() : Current system firmware version : '" + currentVersion + "'.");
							}
                            startUpdateActivity(path, imageFileVersion, currentVersion);
                            return;
                        }else {
                            LOG("find more than two package files, so it is invalid!");
                            return;
                        }
                    }
                }
                	break;

以上程式碼做了這麼幾件事:

1. 確保workHandler只有一個執行緒在操作。

if(mWorkHandleLocked){
	LOG("WorkHandler::handleMessage() : locked !!!");
	return;
}

mWorkHandlerLocked被volatile關鍵字進行了修飾:Volatile關鍵字的理解與使用下篇再做介紹。

2.得到有效的系統升級映象檔案

3.得到映象檔案的版本號和當前系統的版本號。

4.啟動activity.下面是啟動的程式碼

 private void startUpdateActivity(String path, String imageVersion, String currentVersion)
    {
        Intent intent = new Intent(mContext, UpdateAndRebootActivity.class);
//        Intent intent = new Intent();
//        intent.setComponent(new ComponentName("android.rockchip.update.service", "android.rockchip.update.service.UpdateAndRebootActivity"));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(EXTRA_IMAGE_PATH, path);
        intent.putExtra(EXTRA_IMAGE_VERSION, imageVersion);
        intent.putExtra(EXTRA_CURRENT_VERSION, currentVersion);
        mContext.startActivity(intent);
    }

接下來我們去看啟動的UpdateAndRebootActivity。直接定位到關鍵程式碼,我們可以找到onCreate()方法中,程式碼如下:

  wakeLock = null;
        PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
        wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK , TAG);
        if(null != wakeLock) 
        	wakeLock.acquire();
        
        LOG("onCreate() : start 'work thread'.");
        HandlerThread workThread = new HandlerThread("UpdateAndRebootActivity : work thread");   
        workThread.start();
       
        mWorkHandler = new WorkHandler(workThread.getLooper() );
        mUiHandler = new UiHandler();      
        mContext.bindService(new Intent(mContext, RKUpdateService.class), mConnection, Context.BIND_AUTO_CREATE); 

看到這就有必要看一看RKUpdateService。程式碼實現如下

package android.rockchip.update.service;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
import java.util.Locale;

import org.apache.http.Header;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.widget.Toast;
import android.os.SystemProperties;

public class RKUpdateService extends Service {
	public static final String VERSION = "1.7.6";
	private static final String TAG = "RKUpdateService";
    private static final boolean DEBUG = true;
	private static final boolean mIsNotifyDialog = true;
	private static final boolean mIsSupportUsbUpdate = true;
	
	private static final boolean mIsDeleteDirect = true;
	
    private Context mContext;
    private volatile boolean mIsFirstStartUp = true;
    private static void LOG(String msg) {
        if ( DEBUG ) {
            Log.d(TAG, msg);  
        }
    }
    
    static {
        /*
         * Load the library.  If it's already loaded, this does nothing.
         */
        System.loadLibrary("rockchip_update_jni");
    }
    
    public static String OTA_PACKAGE_FILE = "update.zip";
	public static String RKIMAGE_FILE = "update.img";	
	public static final int RKUPDATE_MODE = 1;
	public static final int OTAUPDATE_MODE = 2;
	private static volatile boolean mWorkHandleLocked = false; 
	private static volatile boolean mIsNeedDeletePackage = false;
	
	public static final String EXTRA_IMAGE_PATH = "android.rockchip.update.extra.IMAGE_PATH";
    public static final String EXTRA_IMAGE_VERSION = "android.rockchip.update.extra.IMAGE_VERSION";
    public static final String EXTRA_CURRENT_VERSION = "android.rockchip.update.extra.CURRENT_VERSION";
    public static String DATA_ROOT = "/data/media/0";
    public static String FLASH_ROOT = Environment.getExternalStorageDirectory().getAbsolutePath();
    public static String SDCARD_ROOT = "/mnt/external_sd";
//    public static String SDCARD_ROOT = "/mnt/sdcard";
    public static String USB_ROOT = "/mnt/usb_storage";
    public static String CACHE_ROOT = Environment.getDownloadCacheDirectory().getAbsolutePath();
    
    public static final int COMMAND_NULL = 0;
    public static final int COMMAND_CHECK_LOCAL_UPDATING = 1;
    public static final int COMMAND_CHECK_REMOTE_UPDATING = 2;
    public static final int COMMAND_CHECK_REMOTE_UPDATING_BY_HAND = 3;
    public static final int COMMAND_DELETE_UPDATEPACKAGE = 4;
    
    public static final int COMMAND_UPDATE_FINISH = 5;
    
    private static final String COMMAND_FLAG_SUCCESS = "success";
    private static final String COMMAND_FLAG_UPDATING = "updating";
    
    public static final int UPDATE_SUCCESS = 1;
    public static final int UPDATE_FAILED = 2;
    
    private static final String[] IMAGE_FILE_DIRS = {
    	DATA_ROOT + "/",
        FLASH_ROOT + "/",  
        SDCARD_ROOT + "/",
        USB_ROOT + "/",
    };
    
    private String mLastUpdatePath;
    private WorkHandler mWorkHandler;
    private Handler mMainHandler;
    private SharedPreferences mAutoCheckSet;
   
    /*----------------------------------------------------------------------------------------------------*/
    public static URI mRemoteURI = null;
    public static URI mRemoteURIBackup = null;
    private String mTargetURI = null;
    private boolean mUseBackupHost = false;
    private String mOtaPackageVersion = null;
    private String mSystemVersion = null;
    private String mOtaPackageName = null;
    private String mOtaPackageLength = null;
    private String mDescription = null;
    private volatile boolean mIsOtaCheckByHand = false;
    
	@Override
	public IBinder onBind(Intent arg0) {
		// TODO Auto-generated method stub
		return mBinder;
	}
	
	private final LocalBinder mBinder = new LocalBinder();
	
	public class LocalBinder extends Binder {
		public void updateFirmware(String imagePath, int mode) {
			LOG("updateFirmware(): imagePath = " + imagePath);
	        try {       
				mWorkHandleLocked = true;
				if(mode == OTAUPDATE_MODE){
					RecoverySystem.installPackage(mContext, new File(imagePath));
				}else if(mode == RKUPDATE_MODE){
					RecoverySystem.installRKimage(mContext, imagePath);
				}
	        } catch (IOException e) {
	            Log.e(TAG, "updateFirmware() : Reboot for updateFirmware() failed", e);
	        }
	    }
		
		public boolean doesOtaPackageMatchProduct(String imagePath) {
	      	LOG("doesImageMatchProduct(): start verify package , imagePath = " + imagePath);
			
			try{
				RecoverySystem.verifyPackage(new File(imagePath), null, null);
			}catch(GeneralSecurityException e){
				LOG("doesImageMatchProduct(): verifaPackage faild!");	
				return false;	
			}catch(IOException exc) {
	            LOG("doesImageMatchProduct(): verifaPackage faild!");
				return false;
	        }
	        return true;
	    }
		
		public void deletePackage(String path) {
			LOG("try to deletePackage...");
			File f = new File(path);
			if(f.exists()) {
				f.delete();
				LOG("delete complete! path=" + path);
			}else {
				LOG("path=" + path + " ,file not exists!");
			}
		}
		
		public void unLockWorkHandler() {
			LOG("unLockWorkHandler...");
			mWorkHandleLocked = false;
		}
		
		public void LockWorkHandler() {
			mWorkHandleLocked = true;
			LOG("LockWorkHandler...!");
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();
		
		mContext = this;
        /*-----------------------------------*/
		LOG("starting RKUpdateService, version is " + VERSION);
		
		//whether is UMS or m-user 
		if(getMultiUserState()) {
			FLASH_ROOT = DATA_ROOT;
		}
		
        String ota_packagename = getOtaPackageFileName();
        if(ota_packagename != null) {
        	OTA_PACKAGE_FILE = ota_packagename;
        	LOG("get ota package name private is " + OTA_PACKAGE_FILE);
        }
        
        String rk_imagename = getRKimageFileName();
        if(rk_imagename != null) {
        	RKIMAGE_FILE = rk_imagename;
        	LOG("get rkimage name private is " + RKIMAGE_FILE);
        }
        
        try {
        	mRemoteURI = new URI(getRemoteUri());
        	mRemoteURIBackup = new URI(getRemoteUriBackup());
        	LOG("remote uri is " + mRemoteURI.toString());
        	LOG("remote uri backup is " + mRemoteURIBackup.toString());
        }catch(URISyntaxException e) {
        	e.printStackTrace();
        }
        
        mAutoCheckSet = getSharedPreferences("auto_check", MODE_PRIVATE);
        
        mMainHandler = new Handler(Looper.getMainLooper()); 
        HandlerThread workThread = new HandlerThread("UpdateService : work thread");
        workThread.start();
        mWorkHandler = new WorkHandler(workThread.getLooper());
        
        if(mIsFirstStartUp) {
        	LOG("first startup!!!");
			mIsFirstStartUp = false;
			String command = RecoverySystem.readFlagCommand();
			String path;
			if(command != null) {
				LOG("command = " + command);				
				if(command.contains("$path")) {
					path = command.substring(command.indexOf('=') + 1);
					LOG("last_flag: path = " + path);
				
					if(command.startsWith(COMMAND_FLAG_SUCCESS)) {
						if(!mIsNotifyDialog) {
							mIsNeedDeletePackage = true;
							mLastUpdatePath = path;
							return;
						}

						LOG("now try to start notifydialog activity!");

						Intent intent = new Intent(mContext, NotifyDeleteActivity.class);
						
						if(mIsDeleteDirect)
						{
							File f = new File(path);
							if(f.exists()) 
							{
								f.delete();
								LOG("delete complete! path=" + path);
							}
							else 
							{
								LOG("path=" + path + " ,file not exists!");
							}
							intent.putExtra("deleteDirect", true);
						}
							
						intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						intent.putExtra("flag", UPDATE_SUCCESS);
						intent.putExtra("path", path);
						startActivity(intent);
						
						mWorkHandleLocked = true;
						return;
					} 
					if(command.startsWith(COMMAND_FLAG_UPDATING)) {
						Intent intent = new Intent(mContext, NotifyDeleteActivity.class);
						intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						intent.putExtra("flag", UPDATE_FAILED);
						intent.putExtra("path", path);
						startActivity(intent);
						mWorkHandleLocked = true;
						return;
					}
				}
			}
		}
	}

	@Override
	public void onDestroy() {
		LOG("onDestroy.......");
		super.onDestroy();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		LOG("onStart.......");
		
		super.onStart(intent, startId);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		LOG("onStartCommand.......");
		
		if(intent == null) {
			return Service.START_NOT_STICKY;
		}
		
		int command = intent.getIntExtra("command", COMMAND_NULL);
		int delayTime = 10;
		
//		if(COMMAND_UPDATE_FINISH != command)
			delayTime = intent.getIntExtra("delay", 2000);
//		else {
//			Message msg = new Message();
//			msg.what = command;
//			msg.arg1 = WorkHandler.NOT_NOTIFY_IF_NO_IMG;
//			mWorkHandler.sendMessage(msg);
//			return Service.START_REDELIVER_INTENT;
//		}
		
		LOG("command = " + command + " delaytime = " + delayTime);
		if(command == COMMAND_NULL) {
			return Service.START_NOT_STICKY;
		}
		
		if(command == COMMAND_CHECK_REMOTE_UPDATING) {
			mIsOtaCheckByHand = false;
			if(!mAutoCheckSet.getBoolean("auto_check", true)) {
				LOG("user set not auto check!");
				return Service.START_NOT_STICKY;
			}
		}
		
		if(command == COMMAND_CHECK_REMOTE_UPDATING_BY_HAND) {
			mIsOtaCheckByHand = true;
			command = COMMAND_CHECK_REMOTE_UPDATING;
		}
		
		if(mIsNeedDeletePackage) {
			command = COMMAND_DELETE_UPDATEPACKAGE;
			delayTime = 20000;
			mWorkHandleLocked = true;
		}
		
		Message msg = new Message();
		msg.what = command;
		msg.arg1 = WorkHandler.NOT_NOTIFY_IF_NO_IMG;
		mWorkHandler.sendMessageDelayed(msg, delayTime);
		return Service.START_REDELIVER_INTENT;
	}
   
    
    /** @see mWorkHandler. */
    private class WorkHandler extends Handler {
        private static final int NOTIFY_IF_NO_IMG = 1;
        private static final int NOT_NOTIFY_IF_NO_IMG = 0;
        
        /*-----------------------------------*/
        
        public WorkHandler(Looper looper) {
            super(looper);
        }

        public void handleMessage(Message msg) {       

            String[] searchResult = null;       

            switch (msg.what) {

                case COMMAND_CHECK_LOCAL_UPDATING:
                    LOG("WorkHandler::handleMessage() : To perform 'COMMAND_CHECK_LOCAL_UPDATING'.");
					if(mWorkHandleLocked){
						LOG("WorkHandler::handleMessage() : locked !!!");
						return;
					}
					
                    if ( null != (searchResult = getValidFirmwareImageFile(IMAGE_FILE_DIRS) ) ) 
                    {
                        if ( 1 == searchResult.length ) 
                        { 
                            String path = searchResult[0];      
							String imageFileVersion = null;
							String currentVersion = null;
							
							//if it is rkimage, check the image
                            if(path.endsWith("img")){
								if(!checkRKimage(path)){
									LOG("WorkHandler::handleMessage() : not a valid rkimage !!");
									return;	
								}

								imageFileVersion = getImageVersion(path);

								LOG("WorkHandler::handleMessage() : Find a VALID image file : '" + path 
										+ "'. imageFileVersion is '" + imageFileVersion);
                             
								 currentVersion = getCurrentFirmwareVersion();
								 LOG("WorkHandler::handleMessage() : Current system firmware version : '" + currentVersion + "'.");
							}
                            startProposingActivity(path, imageFileVersion, currentVersion);
                            return;
                        }else {
                            LOG("find more than two package files, so it is invalid!");
                            return;
                        }
                    }
                    
                    break; 
                case COMMAND_CHECK_REMOTE_UPDATING:
                	if(mWorkHandleLocked){
						LOG("WorkHandler::handleMessage() : locked !!!");
						return;
					}
                	
                	for(int i = 0; i < 2; i++) {
	                	try {
	                		boolean result;
	                		
	                		if(i == 0) {
	                			mUseBackupHost = false;
	                			result = requestRemoteServerForUpdate(mRemoteURI);
	                		}else{
	                			mUseBackupHost = true;
	                			result = requestRemoteServerForUpdate(mRemoteURIBackup);
	                		}
	                		
                			if(result) {
                    			LOG("find a remote update package, now start PackageDownloadActivity...");
                    			startNotifyActivity();
                    		}else {
                    			LOG("no find remote update package...");
                    			myMakeToast(mContext.getString(R.string.current_new));
                    		}
                			break;
	                	}catch(Exception e) {
	                		//e.printStackTrace();
	                		LOG("request remote server error...");
	                		myMakeToast(mContext.getString(R.string.current_new));
	                	}
	                	
	                	try{
	                		Thread.sleep(5000);
	                	}catch(InterruptedException e) {
	                		e.printStackTrace();
	                	}
                	}
                	break;
                case COMMAND_DELETE_UPDATEPACKAGE:
                	//if mIsNeedDeletePackage == true delete the package
					if(mIsNeedDeletePackage) {
						LOG("execute COMMAND_DELETE_UPDATEPACKAGE...");
						File f = new File(mLastUpdatePath);
						if(f.exists()) {
							f.delete();
							LOG("delete complete! path=" + mLastUpdatePath);
						}else {
							LOG("path=" + mLastUpdatePath + " ,file not exists!");
						}
						
						mIsNeedDeletePackage = false;
						mWorkHandleLocked = false;
					}
					
                	break;
                
                case COMMAND_UPDATE_FINISH:
                {
                	LOG("WorkHandler::handleMessage() : To perform 'COMMAND_UPDATE_FINISH'.");
					if(mWorkHandleLocked){
						LOG("WorkHandler::handleMessage() : locked !!!");
						return;
					}
					
                    if ( null != (searchResult = getValidFirmwareImageFile(IMAGE_FILE_DIRS) ) ) 
                    {
                        if ( 1 == searchResult.length ) 
                        { 
                            String path = searchResult[0];      
							String imageFileVersion = null;
							String currentVersion = null;
							
							//if it is rkimage, check the image
                            if(path.endsWith("img")){
								if(!checkRKimage(path)){
									LOG("WorkHandler::handleMessage() : not a valid rkimage !!");
									return;	
								}

								imageFileVersion = getImageVersion(path);

								LOG("WorkHandler::handleMessage() : Find a VALID image file : '" + path 
										+ "'. imageFileVersion is '" + imageFileVersion);
                             
								 currentVersion = getCurrentFirmwareVersion();
								 LOG("WorkHandler::handleMessage() : Current system firmware version : '" + currentVersion + "'.");
							}
                            startUpdateActivity(path, imageFileVersion, currentVersion);
                            return;
                        }else {
                            LOG("find more than two package files, so it is invalid!");
                            return;
                        }
                    }
                }
                	break;
                default:
                    break; 
            }
        }

    }  

    private String[] getValidFirmwareImageFile(String searchPaths[]) {
		for ( String dir_path : searchPaths) {
            String filePath = dir_path + OTA_PACKAGE_FILE;    
            LOG("getValidFirmwareImageFile() : Target image file path : " + filePath);
           
            if ((new File(filePath)).exists()) {
                return (new String[] {filePath} );
            }
        }

		//find rkimage
        for ( String dir_path : searchPaths) {
            String filePath = dir_path + RKIMAGE_FILE;
            //LOG("getValidFirmwareImageFile() : Target image file path : " + filePath);
           
            if ( (new File(filePath) ).exists() ) {
                return (new String[] {filePath} );
            }
        }
        
        if(mIsSupportUsbUpdate) {
	        //find usb device update package
	        File usbRoot = new File(USB_ROOT);
	        if(usbRoot.listFiles() == null) {
	        	return null;
	        }
	        
	        for(File tmp : usbRoot.listFiles()) {
	        	if(tmp.isDirectory()) {
	        		File[] files = tmp.listFiles(new FileFilter() {
	
						@Override
						public boolean accept(File arg0) {
							LOG("scan usb files: " + arg0.getAbsolutePath());
							if(arg0.isDirectory()) {
								return false;
							}
							
							if(arg0.getName().equals(RKIMAGE_FILE) || arg0.getName().equals(OTA_PACKAGE_FILE)){
								return true;
							}
							return false;
						}
	        			
	        		});
	        		
	        		if(files != null && files.length > 0) {
	        			return new String[] {files[0].getAbsolutePath()};
	        		}
	        	}
	        }
        }
        
        return null;
    }

    native private static String getImageVersion(String path);

    native private static String getImageProductName(String path);

    private void startProposingActivity(String path, String imageVersion, String currentVersion) {
        Intent intent = new Intent();

        intent.setComponent(new ComponentName("android.rockchip.update.service", "android.rockchip.update.service.FirmwareUpdatingActivity") );
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(EXTRA_IMAGE_PATH, path);
        intent.putExtra(EXTRA_IMAGE_VERSION, imageVersion);
        intent.putExtra(EXTRA_CURRENT_VERSION, currentVersion);

        mContext.startActivity(intent);
    }
    
    private void startUpdateActivity(String path, String imageVersion, String currentVersion)
    {
    	Intent intent = new Intent(mContext, UpdateAndRebootActivity.class);
//    	Intent intent = new Intent();
//    	intent.setComponent(new ComponentName("android.rockchip.update.service", "android.rockchip.update.service.UpdateAndRebootActivity"));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(EXTRA_IMAGE_PATH, path);
        intent.putExtra(EXTRA_IMAGE_VERSION, imageVersion);
        intent.putExtra(EXTRA_CURRENT_VERSION, currentVersion);
        mContext.startActivity(intent);
    }
    
	private boolean checkRKimage(String path){
		String imageProductName = getImageProductName(path);
		LOG("checkRKimage() : imageProductName = " + imageProductName);
		if(imageProductName == null) {
			return false;
		}
		
		if(imageProductName.equals(getProductName())){
			return true;
		}else {
			return false;
		}	
	} 

	private String getOtaPackageFileName() {
		String str = SystemProperties.get("ro.ota.packagename");	
		if(str == null || str.length() == 0) {
			return null;
		}
		if(!str.endsWith(".zip")) {
			return str + ".zip";
		}
		
		return str;
	}
	
	private String getRKimageFileName() {
		String str = SystemProperties.get("ro.rkimage.name");	
		if(str == null || str.length() == 0) {
			return null;
		}
		if(!str.endsWith(".img")) {
			return str + ".img";
		}
		
		return str;
	}
	
    private String getCurrentFirmwareVersion() {    
    	return SystemProperties.get("ro.firmware.version");
    }
    
    private static String getProductName() { 
    	return SystemProperties.get("ro.product.model");        
    }
    
    
    private void notifyInvalidImage(String path) {
        Intent intent = new Intent();

        intent.setComponent(new ComponentName("android.rockchip.update.service", "android.rockchip.update.service.InvalidFirmwareImageActivity") );
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(EXTRA_IMAGE_PATH, path); 

        mContext.startActivity(intent);
    }
    
    private void makeToast(final CharSequence msg) {
    	mMainHandler.post(new Runnable(){  
            public void run(){  
            	Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_LONG).show();
            }  
        });  
    }
    
    /**********************************************************************************************************************
    											ota update
    ***********************************************************************************************************************/
    public static String getRemoteUri() {
    	return "http://" + getRemoteHost() + "/OtaUpdater/android?product=" + getOtaProductName() + "&version=" + getSystemVersion()
    			+ "&sn=" + getProductSN() + "&country=" + getCountry() + "&language=" + getLanguage();
    } 
    
    public static String getRemoteUriBackup() {
    	return "http://" + getRemoteHostBackup() + "/OtaUpdater/android?product=" + getOtaProductName() + "&version=" + getSystemVersion()
    			+ "&sn=" + getProductSN() + "&country=" + getCountry() + "&language=" + getLanguage();
    }
    
    public static String getRemoteHost() {
    	String remoteHost = SystemProperties.get("ro.product.ota.host");
    	if(remoteHost == null || remoteHost.length() == 0) {
    		remoteHost = "192.168.1.143:2300";
    	}
    	return remoteHost;
    }
    
    public static String getRemoteHostBackup() {
    	String remoteHost = SystemProperties.get("ro.product.ota.host2");
    	if(remoteHost == null || remoteHost.length() == 0) {
    		remoteHost = "192.168.1.143:2300";
    	}
    	return remoteHost;
    }
    
    public static String getOtaProductName() {
    	String productName = SystemProperties.get("ro.product.model");
    	if(productName.contains(" ")) {
    		productName = productName.replaceAll(" ", "");
    	}
    	
    	return productName;
    }
    
    public static boolean getMultiUserState() {
    	String multiUser = SystemProperties.get("ro.factory.hasUMS");
    	if(multiUser != null && multiUser.length() > 0) {
    		return !multiUser.equals("true");
    	}
    	
    	multiUser = SystemProperties.get("ro.factory.storage_policy");
    	if(multiUser != null && multiUser.length() > 0) {
    		return mult