Look into it ~

          present
          隨筆 - 32, 文章 - 0, 評論 - 3, 引用 - 0
          數(shù)據(jù)加載中……

          MIDP2.0及MIDP數(shù)字簽名

          本文檔是 WoTrust 根據(jù) Forum Nokia 提供的技術(shù)文檔《MIDP 2.0: Tutorial On Signed MIDlets》翻譯整理的,請同時參考此英文原文文檔。請用戶在編寫 MIDlet 和簽名 MIdlet 之前閱讀此文檔,以便對 MIDP2.0 的安全機制有一個深刻的理解,有助于用戶能用好 MIDlet 代碼簽名證書。

          一、概述

          MIDP2.0 采用了全新的安全機制,這對于需要調(diào)用一個敏感的(重要的)函數(shù)和 API 的 MIDlet 開發(fā)者來講是必須了解的,如:網(wǎng)絡(luò)連接 API 、消息 API 和推 (Push) 函數(shù)等,還有一些可選的 MIDP 包也有許多受限制的 API 。

          雖然購買代碼簽名證書需要費 用,但簽名 MIDlet 對開發(fā)者來講是收益非淺的,因為許多受保護的 API 都是需要簽名的,以保護開發(fā)者和用戶的利益。當(dāng)然,有些應(yīng)用是不需要簽名的,如有些不需要聯(lián)網(wǎng)的僅用到一些圖形 API 的小游戲軟件。但一些重要的應(yīng)用,如:連接網(wǎng)絡(luò)、發(fā)送短消息 ( 短信和彩信 ) 或訪問移動終端 ( 智能手機、 PDA 等,以下簡稱為手機 ) 上的 PIM( 個人信息管理 ) 數(shù)據(jù)等等都需要簽名。

          數(shù)字簽名 MIDlet 的好處包括:

          (1) 基于 MIDlet 的安全策略,某些功能是必須簽名才能使用的,而有些功能雖然不簽名也可以使用,但必須要求用戶在使用時確認和修改其安全策略,如:寫用戶數(shù)據(jù)缺省是不允許沒有簽名的 MIDlet 操作的;

          (2) 基于手機的系統(tǒng)安全和移動網(wǎng)絡(luò)的安全考慮,某些手機制造商、移動運營商等可能拒絕沒有簽名的 MIDlet 在手機上安裝和運行;

          (3) 大大改善用戶體驗,讓用戶使用方便,使得用戶不會遭遇調(diào)用受保護 API 時的安全警告的煩惱;

          (4) 出于安全考慮,安裝沒有簽名的 MIDlet 是會有安全警告的,而相反,安裝已經(jīng)簽名的 MIDlet 則不會出現(xiàn)煩人的警告,手機會自動驗證簽名而順利地安裝成功;

          (5) 已經(jīng)簽名的 MIDlet 將使得用戶能改善其低安全策略設(shè)置,提高手機的安全性;

          (6) 確保已經(jīng)簽名的 MIDlet 不會被非法篡改和非法盜用。

          二、 MIDP 2.0 安全機制

          MIDP 是一個開放的平臺,使得任何人都可以為支持 MIDP 的設(shè)備開發(fā)各種應(yīng)用軟件,一般都是移動終端設(shè)備。 MIDlet 套件可以以匿名方式通過網(wǎng)絡(luò)下載,非常方便,但這也會帶來許多安全問題和隱私信息保護問題,用戶會問: MIDlet 能把用戶的個人信息發(fā)給不知道的服務(wù)器嗎?會自動產(chǎn)生沒有授權(quán)的呼叫或短消息而給用戶帶來費用嗎?惡意軟件會破壞手機?等等。

          除了 Java 語言的安全特性外, MIDP 還增加了許多安全考慮。 MIDP 2.0 比 MIDP 1.0 增強了安全策略,把 API 分為普通 API 和敏感 API ,如:通過 HTTP 協(xié)議訪問移動網(wǎng)絡(luò),由于會給用戶產(chǎn)生費用, 所以被列為 敏感 API 。 MIDlet 2.0 推出了可信任 MIDlet(trusted) 和不可信任 MIDlet(untrusted) 的概念,一個不可信任 MIDlet 只能訪問有限的 API ,同時還需要用戶手動確認并修改其安全策略;而可信任 MIDlet 則自動繼承系統(tǒng)中的安全策略而獲得訪問許可。

          許可 (Permissions) 用于需要身份認證的 敏感 API 。 MIDP 2.0 要求調(diào)用 敏感 API 之前必須獲得必要的許可,這些許可包的命名同 J2SE 許可,如: HTTP 連接許可同樣稱為: javax.microedition.io.Connector.http 。 有關(guān)許可的文檔同意歸類在受保護 API 中。

          2.1 Protection Domains( 保護域 )

          保護域是 MIDP 2.0 中一個非常重要的安全概念,一個保護域就是一個許可集和一種交互模式,這些許可既可以是自己繼承的,也可能是用戶設(shè)置的,前者稱為允許 (allowed) ,而后者稱為用戶允許 (user permission) 。當(dāng)一個 MIDlet 被安裝后,它被分配到一個指定的保護域而獲得它的許可和交互模式。

          而用戶允許則需要用戶自己決定是否同意,用戶既拒絕一個許可,也可以同意。用戶允許有 3 種交互模式: blanket( 普遍適用 ) 、 session( 短期適用 ) 和 oneshot( 本次適用 ) , 普遍適用 模式就是 MIDlet 安裝時獲得的許可一直有效,除非用戶取消這些許可;而 短期適用 模式則是指第一次調(diào)用 API 時需要用戶允許,有效期到此 MIDlet 套件運行結(jié)束;而 本次適用 模式則在每次調(diào)用 API 時都要求用戶允許。保護域為用戶許可定義了缺省的交互模式。

          一個 MIDlet 套件使用 MIDlet-Permissions 和 MIDlet-Permissions-Opt 屬性來明確地定義其許可,可以是在 JAD 文件中定義,也可以在 manifest 文件中定義。其中: MIDlet-Permissions 定義了 MIDlet 套件中必須具有的許可,而 MIDlet-Permissions-Opt 則定義希望具有的許可。如:一個應(yīng)用軟件的基本要求是要有 http 連接才能正常工作,同時,也可以使用 https 連接 ( 服務(wù)器部署了 SSL 證書 ) 來增強安全性,但不是必須的,這樣,這個應(yīng)用軟件的應(yīng)用描述可以是這樣:

          MIDlet-Permissions: javax.microedition.io.Connector.http

          MIDlet-Permissions-Opt: javax.microedition.io.Connector.https

          請注意:一個 MIDlet 所要求的許可必須是安裝時分配的保護域所具有的許可的子集。如: Nokia S60 MIDP Emulator Prototype 2.0 (SDK) 有一個叫做“ minimum ”的域,此域沒有任何許可。所以,如果一個含有許多許可的已經(jīng)簽名的 MIDlet 如果被安裝到此域,則會安裝失敗,因為此域不支持這些許可。同樣,如果一個許可的名稱有拼寫錯誤,則一樣會導(dǎo)致安裝失敗,因為域中沒有此拼寫錯誤的許可。

          MIDP 2.0 為 GSM/UTMS 設(shè)備定義了 4 種保護域: manufacturer( 設(shè)備制造商 ) , operator( 移動運營商 ) , trusted third party( 可信任的第三方 ) , and untrusted( 不受信任域 ) , 除了 untrusted 域外,每個保護域都對應(yīng)一組根證書,用于簽名 MIDlet 的簽名證書的根證書必須包含在這些根證書中,使用不同的簽名證書簽名的 MIDlet 將被自動歸類予根證書所屬的保護域,根證書與保護域的關(guān)系是:一個保護域可以有許多個根證書,而一個根證書只能對應(yīng)于一個保護域。

          具體來講, manufacturer 域?qū)儆谠O(shè)備制造商,其根證書是設(shè)備制造商自己的根證書;而 operator 域運營商,一般使用其 SIM 卡中的根證書;而 trusted third party 域則預(yù)置了全球知名的數(shù)字證書頒發(fā)機構(gòu) (CA) 的根證書,用于驗證由 CA 頒發(fā)的 MIDlet 簽名證書;而 untrusted 域沒有根證書,將用于沒有簽名的 MIDlet 和 MIDP 1.0 。

          Thawte 和 VeriSign 的根證書已經(jīng)預(yù)置在 trusted third party 域 中,其 Java 代碼簽名證書可以用于簽名 MIDlet 。當(dāng)然,用戶也可以選擇使用設(shè)備制造商和移動運營商頒發(fā)的證書,只要其根證書已經(jīng)包含在手機的 4 個保護域中。據(jù) WoTrust 了解,大多數(shù)摩托羅拉 (Motorola) 手機只支持設(shè)備制造商域,所以,只能向 Motorola 申請簽名服務(wù)了。

          請注意:由于 MIDP 2.0 也在不斷地修改和增補,所以,可能不用的移動網(wǎng)絡(luò)運營商有不同的保護域和許可,用戶可能需要向移動運營商了解詳細信息。而最簡單的方法是檢查目標(biāo)用戶所使用的手機的根證書是否有計劃購買的 MIDlet 簽名證書的根證書。

          2.2 Untrusted MIDlet ( 不受信任的 MIDlet)

          MIDP 2.0 定義了那些 API 是 untrusted 的,這些 Jar 文件的來源和完整性是不能被手機驗證的。但這并不意味著這些 MIDlet 不能被安裝和運行,而是運行這些 MIDlet 需要用戶人工確認允許。而所有 MIDP 1.0 的 MIDlets 都被定義為 untrusted 。

          untrusted 的 MIDlets 只能調(diào)用一個不需要許可保護的 API ,如:
          java.util
          java.lang
          java.io
          javax.microedition.rms
          javax.microedition.midlet
          javax.microedition.lcdui
          javax.microedition.lcdui.game
          javax.microedition.media
          javax.microedition.media.control

          如果 untrusted MIDlet 套件試圖調(diào)用一個被保護的 API 而且沒有被人工允許,則會產(chǎn)生一個 SecurityException 而被 MIDlet 按照安全策略處理。請注意: Nokia 的 UI API 是不被保護的,包括類: com.nokia.mid.sound 和 com.nokia.mid.ui 。

          2.3 Trusted MIDlets ( 可信任的 MIDlets)

          如果手機能驗證 MIDlet 的身份和完整性 ( 也就是已經(jīng)數(shù)字簽名 ) ,則會自動分配一個合適的保護 域這種 MIDlet 套件就稱為可信任的 MIDlet 。一個可信任的 MIDlet 套件所要求的許可將被準許,只要所屬的保護域擁有這種許可,假如許可: javax.microedition.io.Connector.http 已經(jīng)在所屬保護域中是允許的,則 MIDlet 在打開一個 http 連接時是不需要用戶確認的。

          請不要混淆了可信任的 MIDlet 套件和可信任的保護域的不同,每個可信任的 MIDlet 套件依據(jù)安全策略被分配到一個特定的保護域。

          您需要使用一個手機中已經(jīng)預(yù)置的根證書的證書頒發(fā)機構(gòu)頒發(fā)的代碼簽名證書來簽名 MIDlet ,否則將不能通過身份驗證。成功簽名后的 JAD 文件中一定會包含有整個簽名證書的證書鏈,屬性名稱為: MIDlet-Certificate-1-1 就是您的簽名證書,而 MIDlet-Certificate-1-2 就是 CA 的中級根證書,而 MIDlet-Certificate-1-3 就是 CA 的頂級根證書。同時還會有一個 MIDlet-Jar-RSA-SHA1 屬性就是 JAR 文件的摘要。

          當(dāng)一個 MIDlet 被下載或被安裝時, MIDlet 應(yīng)用管理器首先會檢查 JAD 文件中是否包含了 MIDlet-Jar-RSA-SHA1 屬 性,如果有,則啟動如下驗證過程:首先會讀出 MIDlet-Certificate-1-1 、 MIDlet-Certificate-1-2 和 MIDlet-Certificate-1-3 屬性中的證書,并與已經(jīng)預(yù)置的根證書相比較,如果證書鏈能被根證書驗證,則表明開發(fā)者身份已經(jīng)被驗證。接著就會使用用戶證書來解密 MIDlet-Jar-RSA-SHA1 屬 性的摘要,再計算出已經(jīng)下載的 Jar 文件的摘要,比較兩個摘要是否相等,如果相等,則表明 MIDlet 代碼自簽名后沒有被修改。這樣,既驗證了身份又檢查了完整性的 MIDlet 會被分配到所屬根證書所對應(yīng)的保護域中。但是,如果 MIDlet 中的許可屬性 ( MIDlet-Permissions ) 中有一個或多個不屬于所屬的保護域,則仍然不允許安裝。而如果 MIDlet 中的可選許可屬性 ( MIDlet-Permissions-Opt ) 中有一個或多個不屬于所屬的保護域,會允許安裝。可見,正確設(shè)置許可屬性和可選許可屬性非常重要。

          2.4 Function Groups ( 功能分組 )

          為了簡化用戶管理操作, MIDlet 把一些類似功能分組,這樣,用戶只需對功能組設(shè)置許可即可。如:許可 “Net Access”( 網(wǎng)絡(luò)訪問 ) 組來代替許可 javax.microedition.io.Connector.http ,這對于簡化手機的交互操作非常有用。

          MIDP 2.0 和 JTWI 定義了如下 7 個功能組:

          (1) Net Access: 包括所有網(wǎng)絡(luò)連接許可;

          (2) Messaging: 包括所有與發(fā)送和接收短消息 ( 短信和彩信 等 ) 相關(guān)的許可;

          (3) Auto Invocation : 包括與自動啟動 MIDlet 相關(guān)的許可,如: Push Registration

          (4) Local Connectivity : 包括與本地連接相關(guān)的許可,如: IrDA 或 藍牙;

          (5) Multimedia Recording : 包括與允許錄音、照相、攝像等相關(guān)的許可;

          (6) Read User Data : 包括讀取用戶數(shù)據(jù)相關(guān)的許可,如:通訊錄、日程表等;

          (7) Write User Data : 包括寫用戶數(shù)據(jù)相關(guān)的許可。

          不同的手機支持不同的功能組,如: Multimedia Recording 就不會包含在沒有攝錄裝置的手機中。當(dāng)然,也有可能將來會增加更多的功能組。

          功能組也同時定義了不同的域的不同交互方式,如:在不信任域, “Net Access” ( 網(wǎng)絡(luò)訪問 ) 被設(shè)置為 session( 短期適用 ) 或 denied( 拒絕 ) ,而在可信任域則可以設(shè)置為 oneshot 、 blanket 和 denied 的。

          三、仿真器和手機的缺省安全設(shè)置

          讓我們來看看具體的使用 Thawte 或 VeriSign 代碼簽名證書簽名后的 MIDlet 在 trusted third party 域中的所有缺省許可,如下圖 1 所示,點擊 NDS 3.0 的“ Config Emulators ”就可以看到仿真器在 trusted third party 域的缺省安全設(shè)置是“ Ask first time ”,即第 1 次使用是需要確認:


          如下圖 2 所示,您可以下拉所有功能組的許可設(shè)置,如“ Network Access ”就有 4 個選項可以修改: Ask first time 、 Ask every time 、 Always allowed 和 Not allowed :


          而如下圖 3 所示,在“ Real Life ”模式,也就是實際手機的運行模式,可以看出:定義的 7 個功能組都是“ Always allowed ” ( 總是允許 ) ,這就顯示出 MIDlet 簽名對于開發(fā)商來講是多么的重要,將大大方便了用戶的使用,再也不需要用戶操作煩人的系列確認了。

          posted @ 2008-08-15 14:53 LukeW 閱讀(318) | 評論 (0)編輯 收藏

          樹形結(jié)構(gòu)

          樹形結(jié)構(gòu)(tree)是比較常用的數(shù)據(jù)結(jié)構(gòu)了,MIDP中沒有它的身影,不然我就不用寫這篇文章了。
          代碼如下:
          /**
           *
           * 
          @author hunhun1981
           
          */
          public class HTree {
           
           
          private HNode root;
           
           
          private HNode current;
           
           
          private int currDepth;
           
           
          private int maxDepth;
           
           
          public HTree(Object rootValue) {
            root 
          = new HNode(null, rootValue);
            current 
          = root;
           }
           
           
          public void goRoot() {
            current 
          = root;
            currDepth 
          = 0;
           }
           
           
          public boolean goChild(int index) {
            
          if (current.childList != null) {
             
          if (current.childList.size() > 0
               
          && index < current.childList.size()) {
              current 
          = (HNode) current.childList.elementAt(index);
              currDepth
          ++;
              
          if (currDepth > maxDepth) {
               maxDepth 
          = currDepth;
              }
              
          return true;
             }
            }
            
          return false;
           }
           
           
          public void goBack() {
            
          if (current.father != null) {
             current 
          = current.father;
             currDepth–;
            }
           }
           
           
          public Object getCurrent() {
            
          return current.value;
           }
           
           
          public int getCurrentDepth() {
            
          return currDepth;
           }
           
           
          public int getMaxDepth() {
            
          return maxDepth;
           }
           
           
          public Object[] getChilds() {
            
          if (current.childList != null) {
             
          if (current.childList.size() > 0) {
              Object[] ret 
          = new Object[current.childList.size()];
              
          for (int i = 0; i < ret.length; i++) {
               ret[i] 
          = ((HNode) current.childList.elementAt(i)).value;
              }
              
          return ret;
             }
            }
            
          return null;
           }
           
           
          public Object getChild(int index) {
            
          if (current.childList != null) {
             
          if (current.childList.size() > 0
               
          && index < current.childList.size()) {
              
          return ((HNode) current.childList.elementAt(index)).value;
             }
            }
            
          return null;
           }
           
           
          public void addChild(Object obj) {
            
          if (current.childList == null) {
             current.childList 
          = new Vector();
            }
            current.childList.addElement(
          new HNode(current, obj));
           }
           
           
          public void addChilds(Object[] objs) {
            
          if (current.childList == null) {
             current.childList 
          = new Vector();
            }
            
          for (int i = 0; i < objs.length; i++) {
             current.childList.addElement(
          new HNode(current, objs[i]));
            }
           }
           
           
          public int hasChild() {
            
          if (current.childList == null || current.childList.size() <= 0) {
             
          return 0;
            } 
          else {
             
          return current.childList.size();
            }
           }
           
           
          private class HNode {
           
            
          public Vector childList;
           
            
          public HNode father;
           
            
          public Object value;
           
            
          public HNode(HNode father, Object value) {
             
          this.value = value;
             
          this.father = father;
             
          this.childList = null;
            }
           }
          }


          這個類實現(xiàn)簡單,沒有包含復(fù)雜的功能,僅僅用來做樹形數(shù)據(jù)的存儲還是不錯的。比如游戲中用來管理場景,管理資源;應(yīng)用中用來作分類數(shù)據(jù)的表現(xiàn)等等。完全足以勝任。
          使用方法如下:
          HTree tree = new HTree(”root”);//會自動創(chuàng)建一個默認的根節(jié)點
          tree.addChild(”天才”);//在根節(jié)點添加新的節(jié)點
          tree.addChild(”白癡”);
          tree.goChild(
          0);//進入到當(dāng)前節(jié)點的第一個節(jié)點(天才)。
          tree.addChild(”天才1號”);//在當(dāng)前節(jié)點(天才)添加新的節(jié)點
          tree.addChild(”天才2號”);
          tree.goBack();
          //返回當(dāng)前節(jié)點(天才)的父節(jié)點(根)
          tree.goChild(1);//進入到當(dāng)前節(jié)點的第二個節(jié)點(白癡)。
          tree.addChild(”白癡1號”);//在當(dāng)前節(jié)點(白癡)添加新的節(jié)點
          tree.addChild(”白癡2號”);
          tree.goRoot();
          //完成創(chuàng)建后將當(dāng)前節(jié)點設(shè)置為根節(jié)點。

          上面的代碼創(chuàng)建了一棵完整的樹,當(dāng)然,您可以使用任何對象代替這里存儲的String對象。
          還有一些方法,一看函數(shù)名大概都能明白,就不再嘮叨了。
          遍歷的方法于上面創(chuàng)建樹的方法相似,總之,要注意當(dāng)前節(jié)點的位置,以免下次使用時處在錯誤的位置。
          有興趣的朋友可以擴展一下遍歷方法。不過我覺得沒必要。因為J2ME環(huán)境下更需要的是樹形結(jié)構(gòu),而不是強大的tree對象。

          總之,我比較傾向于簡單實現(xiàn),希望它不太讓人覺得簡陋就好。從實用出發(fā),它還是能夠滿足大部分受限平臺的需求的。

          posted @ 2008-08-15 14:51 LukeW 閱讀(199) | 評論 (0)編輯 收藏

          URLEncoding

          URLEncoding是用于解決鏈接字符串中包含中文字符的一種轉(zhuǎn)換編碼。各種編程環(huán)境下幾乎帶有它的庫函數(shù)。

          不過,J2ME除外。

          好在JAVA的源代碼中帶有這個類,我們把它拷貝到J2ME環(huán)境下編譯到我們的應(yīng)用當(dāng)中就可以了。

          該文件位于JDK的目錄下src.zip文件中,名叫URLEncoder.java。

          但是,這個文件還需要做很多修改才能使用在J2ME環(huán)境中。

          先警告大家,有幾個真機(其中一個就是索愛的,好像是k500c),不管輸入什么樣的Encodeing都會出錯,甚至是“UTF-8”。所以我一怒之下 去除了Encodeing參數(shù)。(這可是在實際應(yīng)用中得出的結(jié)論,不去掉的話可以在大部分情況下正常使用,但是,現(xiàn)實總是有點缺陷)

          修改后的代碼如下,大家請放心使用。如果有興趣,可以比較兩個代碼,看看我改動了什么地方。


          import java.io.ByteArrayOutputStream;
          import java.io.DataOutputStream;
          public class HURLEncoder {
           
           
          private static boolean[] dontNeedEncoding;
           
           
          static {
            dontNeedEncoding 
          = new boolean[256];
           
            
          for (int i = 0; i < 256; i++) {
             
          boolean b = ((i >= ‘0′) && (i <= ‘9′))
               
          || ((i >= ‘A’) && (i <= ‘Z’)) || ((i >= ‘a’) && (i <= ‘z’));
           
             dontNeedEncoding[i] 
          = b;
            }
           
            dontNeedEncoding[’ ‘] 
          = true;
            dontNeedEncoding[’
          -'] = true;
            dontNeedEncoding[’_'] = true;
            dontNeedEncoding[’.'] = true;
            dontNeedEncoding[’*'] = true;
           }
           
           
          public static String encode(String s) {
           
            
          boolean wroteUnencodedChar = false;
           
            StringBuffer writer 
          = new StringBuffer();
           
            StringBuffer out 
          = new StringBuffer(s.length());
           
            
          for (int i = 0; i < s.length(); i++) {
             
          char c = s.charAt(i);
           
             
          if ((c < 256&& dontNeedEncoding[c]) {
              
          if (c == ‘ ‘) {
               c 
          = ‘+’;
              }
           
              out.append((
          char) c);
              wroteUnencodedChar 
          = true;
             } 
          else {
              
          try {
               
          if (wroteUnencodedChar) {
                writer 
          = new StringBuffer();
                wroteUnencodedChar 
          = false;
               }
           
               writer.append(c);
           
               
          if (c >= 0xD800 && c <= 0xDBFF) {
                
          if ((i + 1< s.length()) {
                 
          int d = (int) (s.charAt(i + 1));
           
                 
          if (d >= 0xDC00 && d <= 0xDFFF) {
                  writer.append(d);
                  i
          ++;
                 }
                }
               }
           
              } 
          catch (Exception e) {
               writer 
          = new StringBuffer();
               
          continue;
              }
           
              String str 
          = writer.toString();
           
              ByteArrayOutputStream baos 
          = new ByteArrayOutputStream();
              DataOutputStream dos 
          = new DataOutputStream(baos);
              
          try {
               dos.writeUTF(str);
               dos.flush();
              } 
          catch (Exception e) {
               e.printStackTrace();
              }
           
              
          byte[] temp = baos.toByteArray();
              
          byte[] ba = new byte[temp.length - 2];
              
          for (int ix = 0; ix < ba.length; ix++) {
               ba[ix] 
          = temp[ix + 2];
              }
           
              
          for (int j = 0; j < ba.length; j++) {
               out.append(’
          %');
           
               
          char ch = forDigit((ba[j] >> 4& 0xF16);
               out.append(ch);
           
               ch 
          = forDigit(ba[j] & 0xF16);
               out.append(ch);
              }
           
              writer 
          = new StringBuffer();
              
          try {
               dos.close();
               baos.close();
              } 
          catch (Exception e) {
               e.printStackTrace();
              }
             }
            }
           
            
          return out.toString();
           }
           
           
          private static char forDigit(int digit, int radix) {
            
          if ((digit >= radix) || (digit < 0)) {
             
          return ‘0′;
            }
            
          if (digit < 10) {
             
          return (char) (’0′ + digit);
            }
            
          return (char) (’A’ + digit - 10);
           }


          posted @ 2008-08-15 14:49 LukeW 閱讀(1509) | 評論 (0)編輯 收藏

          GB2312轉(zhuǎn)換為UTF-8

          摩托羅拉的部分手機(a1200,e60等),不支持gb2312編碼。曾經(jīng)給我造成了不少麻煩。現(xiàn)在,大家可以分享解決這個問題的一些經(jīng)驗。

          關(guān)于gb2312,unicode,utf-8的一些資料,大家請自行搜索。一下列舉幾個比較好的資源網(wǎng)址。
          http://baike.baidu.com/view/25492.htm
          http://www.utf.com.cn/article/s45
          http://www.utf.com.cn/article/s74
          http://www.haiyan.com/steelk/navigator/ref/gb2312/gbindex.htm

          要點:
          1,gb2312于unicode或者utf-8之間并不存在直接的映射關(guān)系。所以我們只能通過查表法來進行轉(zhuǎn)換。
          2,utf-8是unicode用于網(wǎng)絡(luò)傳輸?shù)囊环N形式,它與unicode之間是可以通過運算來進行轉(zhuǎn)換的。
          3,j2me環(huán)境使用的都是utf-8編碼,但是請注意,j2me中的utf-8編碼比較特殊,在整個編碼前面對了兩個字節(jié),用于存放字符串的長度。

          過程:
          1,制作映射表gb2312-unicode,應(yīng)為漢字的unicode比utf-8要小,這樣做出的表也會小一些,而且對于unicode的可擴展性也強一些。
          2,先將gb2312編碼串通過查表,轉(zhuǎn)換為unicode。
          3,然后通過運算,將unicode轉(zhuǎn)換為utf-8,以便在j2me環(huán)境下使用。

          我修改了Herong Yang大俠的一個映射表生成函數(shù),原文請參考http://www.herongyang.com/gb2312/gb2312_unicode.html
          它的作用是生成一個二進制的gb2312到unicode的查找表,它按照gb2312的分區(qū),分塊特性,將其對應(yīng)的unicode按順序存入指定的位置。
          這樣我們只需要根據(jù)gb2312的編碼,計算出索引就可以獲取編碼對應(yīng)的unicode了。
          由于是修改的代碼,沒臉貼出來,大家有需求可以直接參考Herong Yang的文章,然后根據(jù)自己需求修改并生成自己的映射表。

          這里我把自己這個轉(zhuǎn)換表文件以及訪問代碼公開。
          http://download.csdn.net/source/263609
          轉(zhuǎn)帖請注明。這是個傻瓜化的代碼,在java中給它gb2312的byte數(shù)組,它就給你構(gòu)造出字符串。
          用在不支持gb2312的手機上非常方便。這個轉(zhuǎn)換表的大小是15228byte,對j2me來說還是可以接受的。

          如果有朋友需要溝通,可以發(fā)郵件到hunhun1981@hotmail.com

          import java.io.InputStream;
           
          public class HGB2312 {
           
              
          private byte[] map = new byte[15228];
           
              
          private byte[] buffer;
              
          private int index;
           
              
          public HGB2312() throws Exception {
                  InputStream is 
          = getClass().getResourceAsStream("/gb2u.dat");
                  is.read(map);
                  is.close();
              }
           
              
          public String gb2utf8(byte[] gb) throws Exception {
                  buffer 
          = new byte[gb.length + gb.length / 2 + 3];
                  index 
          = 0;
                  
          int c, h, l, ind;
                  
          for (int i = 0; i < gb.length;) {
                      
          if (gb[i] >= 0) {
                          fillBuffer(gb[i
          ++]);
                      } 
          else {
                          h 
          = 256 + gb[i++];
                          l 
          = 256 + gb[i++];
                          h 
          = h - 0xA0 - 1;
                          l 
          = l - 0xA0 - 1;
                          
          if (h < 9) {
                              ind 
          = (h * 94 + l) << 1;
                              c 
          = (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                              fillBuffer(c);
                          } 
          else if (h >= 9 && h <= 14) {
                              fillBuffer(
          0);
                          } 
          else if (h > 14) {
                              h 
          -= 6;
                              ind 
          = (h * 94 + l) << 1;
                              c 
          = (byte2Int(map[ind]) << 8 | byte2Int(map[ind + 1]));
                              fillBuffer(c);
                          } 
          else {
                              fillBuffer(
          0);
                          }
                      }
                  }
                  
          // ind = index - 2;
                  
          // h = (byte) ((ind >> 8) & 0x7F);
                  
          // l = (byte) (ind & 0xFF);
                  
          // buffer[0] = h;
                  
          // buffer[1] = l;
           
                  
          return new String(buffer, 0, index, "UTF-8");
              }
           
              
          private void fillBuffer(int value) {
                  
          if (value <= 0x0000007F) {
                      buffer[index
          ++= (byte) value;
                  } 
          else if (value >= 0x00000080 && value <= 0x000007FF) {
                      
          byte b1 = (byte) (0x60 | (value >> 6));
                      
          byte b2 = (byte) (0x80 | (value & 0x3F));
                      buffer[index
          ++= b1;
                      buffer[index
          ++= b2;
                  } 
          else if (value >= 0x00000800 && value <= 0x0000FFFF) {
                      
          byte b1 = (byte) (0xE0 | (value >> 12));
                      
          byte b2 = (byte) (0x80 | ((value >> 6& 0x3F));
                      
          byte b3 = (byte) (0x80 | (value & 0x3F));
                      buffer[index
          ++= b1;
                      buffer[index
          ++= b2;
                      buffer[index
          ++= b3;
                  } 
          else if (value >= 0x00010000 && value <= 0x001FFFFF) {
                      
          byte b1 = (byte) (0x1E | (value >> 18));
                      
          byte b2 = (byte) (0x80 | ((value >> 12& 0x3F));
                      
          byte b3 = (byte) (0x80 | ((value >> 6& 0x3F));
                      
          byte b4 = (byte) (0x80 | (value & 0x3F));
                      buffer[index
          ++= b1;
                      buffer[index
          ++= b2;
                      buffer[index
          ++= b3;
                      buffer[index
          ++= b4;
                  } 
          else if (value >= 0x00200000 && value <= 0x03FFFFFF) {
                      
          byte b1 = (byte) (0x3E | (value >> 24));
                      
          byte b2 = (byte) (0x80 | ((value >> 18& 0x3F));
                      
          byte b3 = (byte) (0x80 | ((value >> 12& 0x3F));
                      
          byte b4 = (byte) (0x80 | ((value >> 6& 0x3F));
                      
          byte b5 = (byte) (0x80 | (value & 0x3F));
                      buffer[index
          ++= b1;
                      buffer[index
          ++= b2;
                      buffer[index
          ++= b3;
                      buffer[index
          ++= b4;
                      buffer[index
          ++= b5;
                  } 
          else if (value >= 0x04000000 && value <= 0x7FFFFFFF) {
                      
          byte b1 = (byte) (0x7E | (value >> 30));
                      
          byte b2 = (byte) (0x80 | ((value >> 24& 0x3F));
                      
          byte b3 = (byte) (0x80 | ((value >> 18& 0x3F));
                      
          byte b4 = (byte) (0x80 | ((value >> 12& 0x3F));
                      
          byte b5 = (byte) (0x80 | ((value >> 6& 0x3F));
                      
          byte b6 = (byte) (0x80 | (value & 0x3F));
                      buffer[index
          ++= b1;
                      buffer[index
          ++= b2;
                      buffer[index
          ++= b3;
                      buffer[index
          ++= b4;
                      buffer[index
          ++= b5;
                      buffer[index
          ++= b6;
                  }
              }
           
              
          private int byte2Int(byte b) {
                  
          if (b < 0) {
                      
          return 256 + b;
                  } 
          else {
                      
          return b;
                  }
              }
          }

          posted @ 2008-08-15 14:47 LukeW 閱讀(634) | 評論 (0)編輯 收藏

          修改png圖的調(diào)色板

          今天在硬盤上挖出這個存放了幾年的代碼。又回憶起3年前的那個j2me手機游戲程序員……

          這個算法是參考一位高人的文章,直接讀取并修改png格式圖片的調(diào)色板,然后生成新的調(diào)色板替代原來的。
          這樣可以實現(xiàn)游戲中常見的變色效果,可以解決游戲容量有限,不能存放太多精靈圖片的問題。

          具體過程其實并不復(fù)雜,大家可以先搜索資料,先看看png圖片的格式定義。這個算法正是找到調(diào)色板區(qū),根據(jù)原有格式修改之后,生成新的crc校驗碼,然后替換原來的調(diào)色板。這樣就可以用一個png圖片,創(chuàng)建多個變色副本。
          public class PalettedImage {
           
              
          public Image getPalettedImage(byte[] data, int[] originalColors,
                      
          int[] palettedColors) {
                  
          byte[] tempData = new byte[data.length];
                  System.arraycopy(data, 
          0, tempData, 0, data.length);
                  Image img 
          = null;
                  
          int[] parameter = { 000 };
                  analyze(tempData, parameter);
                  
          for (int i = 0; i < originalColors.length; i++) {
                      replaceColor(tempData, parameter, originalColors[i],
                              palettedColors[i]);
                  }
                  fillData(tempData, parameter);
                  
          try {
                      img 
          = Image.createImage(tempData, 0, data.length);
                  } 
          catch (Exception e) {
                      System.out.println(
          "getPalettedImage  &&  " + e.toString());
                  }
                  
          return img;
              }
           
              
          private void analyze(byte[] data, int[] para) {
                  
          int offset = 8;
                  
          int chunkLen = 0;
                  
          while (data[offset + 4!= 0x50 || data[offset + 5!= 0x4c
                          
          || data[offset + 6!= 0x54 || data[offset + 7!= 0x45) {
                      chunkLen 
          = readInt(data, offset);
                      offset 
          += (4 + 4 + chunkLen + 4);
                  }
                  chunkLen 
          = readInt(data, offset);
                  para[
          2= chunkLen / 3;
                  para[
          0= offset + 8;
                  para[
          1= offset + 8 + chunkLen;
              }
           
              
          private int readInt(byte[] data, int offset) {
                  
          return ((data[offset] & 0xFF<< 24)
                          
          | ((data[offset + 1& 0xFF<< 16)
                          
          | ((data[offset + 2& 0xFF<< 8| (data[offset + 3& 0xFF);
              }
           
              
          private void replaceColor(byte[] data, int[] para, int oldColor,
                      
          int newColor) {
                  
          byte rr = (byte) ((oldColor >> 16& 0xff);
                  
          byte gg = (byte) ((oldColor >> 8& 0xff);
                  
          byte bb = (byte) (oldColor & 0xff);
                  
          for (int i = 0, offset = para[0], temp = 0; i < para[2]; i++, offset += 3) {
                      
          if (rr == data[offset] && gg == data[offset + 1]
                              
          && bb == data[offset + 2]) {
                          data[offset] 
          = (byte) ((newColor >> 16& 0xff);
                          data[offset 
          + 1= (byte) ((newColor >> 8& 0xff);
                          data[offset 
          + 2= (byte) (newColor & 0xff);
                          
          break;
                      }
                  }
              }
           
              
          private void fillData(byte[] data, int[] para) {
                  
          int checksum = update_crc(data, para[0- 4, para[2* 3 + 4);
                  data[para[
          1]] = (byte) ((checksum >> 24& 0xff);
                  data[para[
          1+ 1= (byte) ((checksum >> 16& 0xff);
                  data[para[
          1+ 2= (byte) ((checksum >> 8& 0xff);
                  data[para[
          1+ 3= (byte) ((checksum) & 0xff);
              }
           
              
          private int update_crc(byte[] buf, int off, int len) {
                  
          int c = 0xffffffff;
                  
          int n, k;
                  
          int xx;
                  
          int[] crc_table = new int[256];
                  
          for (n = 0; n < 256; n++) {
                      xx 
          = n;
                      
          for (k = 0; k < 8; k++) {
                          
          if ((xx & 1== 1) {
                              xx 
          = 0xedb88320 ^ (xx >>> 1);
                          } 
          else {
                              xx 
          = xx >>> 1;
                          }
                      }
                      crc_table[n] 
          = xx;
                  }
           
                  
          for (n = off; n < len + off; n++) {
                      c 
          = crc_table[(c ^ buf[n]) & 0xff^ (c >>> 8);
                  }
                  
          return (c ^ 0xffffffff);
              }
           
          }


          接口就是getPalettedImage()函數(shù),只需要輸入原始圖片的byte數(shù)組,以及需要替換顏色的顏色值還有目標(biāo)顏色值就行了。因為可以同時替換多個顏色,所以輸入?yún)?shù)是代表顏色的整形的數(shù)組。總之,要保證原始顏色與目標(biāo)顏色一一對應(yīng)就好了。方法簡單實用。

          歡迎大家使用并留下寶貴的意見。當(dāng)然,也可以修改一下這個函數(shù),做一些特殊的效果。這里就不多說了。
          不過這個代碼用處已經(jīng)不大,因為現(xiàn)在的手機基本上都支持midp2.0所以可以使用更方便的方法替換顏色。

          總之,再次感謝這位已經(jīng)被我忘掉名字的大俠,關(guān)鍵代碼是他寫的,我只是修改整理而已。

          posted @ 2008-08-15 14:34 LukeW 閱讀(616) | 評論 (0)編輯 收藏

          Autostarting MIDlets in JP-7 phones using PushRegistry

          In Sony Ericsson Java Platform 7 (JP-7) phones, such as the K610 or W850, a new push functionality is introduced. PushRegistry Alarm and PushRegistry SMS are supported throughout the Java Platforms, with JP-4 adding PushRegistry CBS and now JP-7 includes PushRegistry auto start.

          Note: the auto start functionality is not supported by the first released K610 and K800 JP-7 phones. Please use the phone's upgrade service to ensure that you are using the latest firmware version.

          The auto start functionality can be set static by including it in the .jad file or it can be set dynamically in the code. Two example MIDlets are included as an example of this.

          Download the source code and examples here>>

          To enable the auto start functionality in the .jad file include the line:
          //MIDlet-Push-<n>: <ConnectionURL>, <MIDletClassName>, <AllowedSender>
          MIDlet-Push-1: autostart://:, AutoStartStatic, *


          To make the MIDlet auto start from the source code the following methods can be used to register and un-register the MIDlet.
          //Registers the pushRegistry
          public void Register(){
                  
          // List of registered push connections.
                  String connections[];
                  
          // Check to see if the connection has been registered.
                  
          // This is a dynamic connection allocated on first
                  
          // time execution of this MIDlet.
                  connections = PushRegistry.listConnections(false);
                  
          if (connections.length == 0) {
                          
          try {
                                  
          //Register so the MIDlet will wake up when phone is started.
                                  PushRegistry.registerConnection("autostart://:""AutoStartDyn""*");
                                  sDisplayString 
          = "MIDlet is registered";
                          } 
          catch (Exception ex) {
                                  System.out.println(
          "Exception: " + ex);
                                  sDisplayString 
          = "Fail: " + ex;
                          }
                  } 
          else {
                          sDisplayString 
          = "Already registered";
                  }
                  displayForm.deleteAll();
                  displayForm.append(sDisplayString);
          }
           
          //Unregisters the pushRegistry
          public void Unregister(){
                  
          if (PushRegistry.unregisterConnection("autostart://:")){
                          System.out.println(
          "The pushRegistry is unregistered");
                          sDisplayString 
          = "MIDlet is unregistered.";
                  }
          else{
                          System.out.println(
          "There is no pushRegistry to unregister");
                          sDisplayString 
          = "No MIDlet to unregister or failed to unregister";
                  }
                  displayForm.deleteAll();
                  displayForm.append(sDisplayString);
          }



          To find out if the MIDlet is started via pushRegistry or manually you can inspect the connections registered by pushRegistry. Below is a small example of how to handle pushRegistry autostart or manual startup.
              private void handlePushActivation() {
                  String[] connections 
          = PushRegistry.listConnections(true);
                  
          if (connections != null && connections.length > 0) {
                      
          for (int i = 0; i < connections.length; i++) {
                          
          if (connections[i].startsWith("autostart")) {
                              alert(
          "Application autostarted");
                          }
                      }
                  } 
          else {
                      alert(
          "User started the application");
                  }
              }

          posted @ 2008-08-15 10:57 LukeW 閱讀(350) | 評論 (0)編輯 收藏

          Using simultaneous sounds

          The code sample below describes how to play two sounds at the same time. This feature is supported by the Sony Ericsson JP-5 platform and onwards.

          Only one wav file can be played simultaniously but several midi files might be played at the same time. The number of existing players is only limited by available memory, and you have the possibility to play more than one player at the same time. The example below shows how to do this using one midi file and one wav file.

          The code is straight-forward - just load the resource, create the player and start playing the file.

          Here's the sample:
          InputStream is = getClass().getResourceAsStream(file);
          InputStream is1 
          = getClass().getResourceAsStream(file1);

          player 
          = Manager.createPlayer(is, " audio/midi");
          player.setLoopCount(
          -1);
          player.prefetch();
          player.realize();

          player1 
          = Manager.createPlayer(is1, "audio/x-wav");
          player1.setLoopCount(
          1);
          player1.prefetch();
          player1.realize();

          player.start();
          player1.start();

          example code

          posted @ 2008-08-15 10:34 LukeW 閱讀(121) | 評論 (0)編輯 收藏

          Serializing an Image

          Creating an image from an array of data is an easy task, but to create a byte-array of data from an image is a little more complicated. But it's required if you want to send a modified image to a server.

          To create a byte-array of data from an image, we can use the getRGB(..) method in the image class in MIDP 2.0. From the getRGB method we get an int-array of data containing all the ARGB values of each pixel in the image. Integers in java are four bytes, so we need to split each int value into four byte values.

          To mask out each of the bytes in the int we can use the 'AND &' operator and the 'shift-right >>' operator. Here's an example:
          int ARGB = 0xFFFFFFFF;  // AARRGGBB 
          int a = (ARGB & 0xFF000000); 
          int r = (ARGB & 0x00FF0000); 
          int g = (ARGB & 0x0000FF00); 
          int b = (ARGB & 0x000000FF);
           
          // Here we move each bit to the right.
          = (a >> 24); // a = 0x000000FF 
          = (r >> 16); // r = 0x000000FF 
          = (g >> 8); // g = 0x000000FF
          = b;        // b = 0x000000FF


          When we convert the integer to a byte, there are some problems since there are only signed bytes in Java. A byte may contain values between -128 and 127 and from our integer we'll have a value between 0 and 255.

          Here are some conversion examples:

          Integer val 
          127 = byte val 127.
          Integer val 
          128 = byte val -128.
          Integer val 
          255 = byte val -1.

          byte ba = (byte)(a);  // if a=0x000000FF (255), then ba = -1
          byte br = (byte)(r); 
          byte bg = (byte)(g); 
          byte bb = (byte)(b);


          We have to loop though each pixel in the image and get each pixel value to our byte-array. When that's done, we can send the image to a server where we can convert the byte-array back to a integer-array and then show our picture.

          So, when we convert the byte back to an integer we have to check if the byte value is less than zero.
          int a, r, g, b;
          if(ba<0)
               a 
          = 256 - a;


          Now our new integer value will be between 0 and 255 and we just have to use the 'shift-left <<' operator and add the values together to get our new int-array.
          = (a << 24);
          = (r << 16);
          = (g << 8);
          = (b);
           
            
          0xFF000000 (a)
          + 0x00FF0000 (r)
          + 0x0000FF00 (g)
          + 0x000000FF (b)
          = 0xFFFFFFFF (argb)
           
          int ARGB = a+r+g+b;

          After the integer-array is recreated we can use the createRGBImage(..) method in the Image class to create our image.

          Be aware that the byte-array is quite large as it contains all of the ARGB values for each pixel. If an image is 100*60 px, where each pixel is four bytes, the byte array will be 24kb.
          example code

          posted @ 2008-08-15 10:29 LukeW 閱讀(152) | 評論 (0)編輯 收藏

          Fade in and out images in MIDP 2.0

          This tip describes how to change the alpha value of an image to make it appear blended. There's also an example MIDlet with source code.

          In MIDP 2.0 there's a new method in the Image class, getRGB(...) that will get all Alpha, Red, Green, Blue (ARGB) values from the image to an int array. We can use this method, and the resulting array, to change the alpha value of the image.

          Integers in J2ME are four bytes, each pixel in the image is described by the ARGB values where each color is one byte 0 to 255. If the alpha value is 0, the corresponding pixel will be transparent, if the alpha is 255, the pixel will be opaque.

          To get a specific color from the int array, it's possible to use the AND '&' operator and to accomplish the blending effect we need to get the colors, without the alpha value, from the int array.
          FF = 11111111 = 255
          0xFFFFFFFF - Alpha = 255, Red =255 Green = 255, Blue = 255
          (
          0xFFFFFFFF & 0x00FFFFFF= 0x00FFFFFF


          By doing this we'll only get the RGB colors from the int array, alpha equals zero.

          Now when we just have the RGB colors and alpha equals zero, we can just add our new alpha value.

          If we have the alpha value 255 and want to add this to our color, we need to use the shift left operator.

          To change:
          (00000000 00000000 00000000 11111111) to 
          (
          11111111 00000000 00000000 00000000)
          use the shift left 
          '<<' operator.
          (
          0xFF << 24= 0xFF000000.


          With this knowledge we now can change the alpha value or do masking for specific colors.

          • Use the getRGB(...) method in the image class to get all the ARGB values from the image to a int array.
          • Use the blend function below to change the alpha value for each value in the array.
          • Use the createRGBImage(...) method in the image class to create a new image from the edited int array.

          There are examples on how to use the getRGB and createRGBImage methods in the MIDlet below.
          public static void blend(int[] raw, int alphaValue){
              
          int len = raw.length;
              
          // Start loop through all the pixels in the image.
              for(int i=0; i<len; i++){
                  
          int a = 0;
                  
          int color = (raw[i] & 0x00FFFFFF); // get the color of the pixel.
                  a = alphaValue;     // set the alpha value we want to use 0-255.
           
                  a 
          = (a<<24);    // left shift the alpha value 24 bits.
                  
          // if color = 00000000 11111111 11111111 00000000 (0xFFFF00 = Yellow)
                  
          // and alpha= 01111111 00000000 00000000 00000000
                  
          // then c+a = 01111111 11111111 11111111 00000000
                  
          // and the pixel will be blended.
                  color += a;
                  raw[i] 
          = color;
              }
          }

          example code

          posted @ 2008-08-15 10:18 LukeW 閱讀(199) | 評論 (0)編輯 收藏

          Fast stream reading in Java

          To increase the performance of your Java™ application when reading from an InputStream, there are a few key areas to look into. If possible, don't make any reallocations of memory. Allocate the input buffer once. Let the Java™ VM do the bulk of the reading, i.e. read data in big chunks. Some coding examples show a loop that reads data a small amount at a time. Using a too small buffer is inefficient. A much better technique is to use a relatively large buffer.

          If you know the maximum content length, the most optimal read would be a single line, like this:
          len = instream.read( buf, 0, MAX_BUFFER_SIZE );

          In this case we assume that the buffer has already been allocated with a size set to MAX_BUFFER_SIZE.

          If the content is of varying size, we have to make a tradeoff between performance and memory usage. If you keep your buffer size just above the average content length, then the number of reallocations of the data buffer and the number of reads will be kept at a minimum. There is no defined size of what a large buffer is but a good rule of thumb might be to keep the buffer size at most about 0,5 to 1 MB.

          posted @ 2008-08-15 10:11 LukeW 閱讀(145) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共3頁: 上一頁 1 2 3 
          主站蜘蛛池模板: 枣阳市| 墨竹工卡县| 东丰县| 昆明市| 民县| 萝北县| 固原市| 永清县| 新野县| 尚志市| 渝北区| 阿克| 曲阜市| 二连浩特市| 绍兴县| 建阳市| 贺州市| 万盛区| 京山县| 临安市| 五莲县| 衢州市| 库尔勒市| 楚雄市| 华宁县| 禹城市| 祁东县| 淮南市| 临洮县| 宁乡县| 辽中县| 岐山县| 辽宁省| 安仁县| 温泉县| 武平县| 邹城市| 秭归县| 昭苏县| 斗六市| 望都县|