Android のなかの小さなクラウド

ブラウザーから Android ファイルシステムを探索する

クラウド・コンピューティングに最小限必要なコンポーネントは、ポータブル・デバイス上で動作するクライアント・ソフトウェアと、通常はネットワーク・サーバー上で動作するサーバー・ソフトウェアの 2 つだけです。この記事では、ネットワーク・サーバーをエミュレートする Android ベースのサービスを作成し、それによって独特かつ思いもよらない方法でローカル・マシンの価値を高めることを提案します。Android 搭載の携帯電話のなかに小さなクラウドを組み込んで、ローカル Web サーバーとしてのその有用性を体験してください。

Bill Zimmerly, Freelance Writer and Knowledge Engineer, Author

Bill ZimmerlyBill Zimmerly はナレッジ・エンジニアであると同時に、さまざまなバージョンの UNIX および Microsoft® Windows® の専門知識を持つ下位レベルのシステム・プログラマーであり、Logic の祭壇で礼拝する自由な考えの持ち主です。彼は新しい技術を創出し、それらの技術について説明することに情熱を注いでいます。彼が住んでいるのは、空気は新鮮で景色は霊感を与えるほど美しく、そして素晴らしいワインが年中手に入るアメリカ合衆国ミズーリ州のヒルズボロです。



2009年 11月 17日

Android は、極めて高機能なスマートフォンに要求される機能を管理するために設計された、Linux® オペレーティング・システムの特殊なディストリビューションです。Linux に含まれるすべてのもの (デバイスからスプレッドシート、さらにはお気に入りの歌まで) は、プログラマーの目にはファイルとして映ります。つまり、これらの多種多様な抽象的エンティティーに含まれる情報は、すべて共通の方法で表示、抽出、操作、保存できるということです。そしてこの共通性は、UNIX® の基本思想の中核をなす「すべてのものはファイルである」という原則に基づいたものです。

よく使われる頭字語

  • MIME: Multipurpose Internet Mail Extension
  • HTML: HyperText Markup Language
  • SDK: Software Development Kit
  • WWW: World Wide Web

ファイルは、ファイルシステムと呼ばれる実用的な階層に分類されます。Android 携帯電話では通常、2 つのファイルシステムを使用します。1 つは携帯電話の不揮発性メモリー、もう 1 つは携帯電話に挿入される SD カードで、どちらもルート・ディレクトリー (/) の分岐のように見えます。これらのファイルシステムをブラウザーから簡単に参照できるようにするツールがあれば、役に立つこと間違いありません。そこで、この記事のプロジェクトでは Web サーバーとして通信を行う小さなプログラム (ネイティブ C 言語で作成) を定義し、このプログラムによって Android 携帯電話のファイルシステムをワークステーションから、あるいは携帯電話自体の組み込みブラウザーから直接参照できるようにします。階層型ツリーを上下に移動するには、ページに表示されたハイパーリンクを使用します。また、表示されたさまざまなファイル・タイプをクリックすると、個々の項目が表示されるようにします。

開発環境のセットアップ手順

まずは、「ルート化」した Android 携帯電話を用意し、ターミナル・ソフトを起動して su コマンドを実行することでルート権限を取得するといった操作を確実に行えるようにします。Android 携帯電話を「ルート化」してルート権限を取得する方法がわからないとしても、インターネットを検索すればすぐに必要な情報を入手することができます (このプロセスは、携帯電話の Jailbreak (脱獄) と呼ばれています。その方法については、「参考文献」に記載されたリンクを参照してください)。

Android コミュニティーでは主に 2 つの SDK を使用します。このうち、よく知られているほうの高機能の Android SDK では、アプリケーションのコードを Java™ 言語で作成することができます。また、コードの作成、テスト、デバッグには共通して Eclipse を使用します。もう一方のそれほどよく知られていない SDK は、その Android カーネル・ソースが git リポジトリーに保管されています (「参考文献」に、このリポジトリーへのリンクが記載されています)。

この記事で焦点とするのは、一般に携帯電話の system/bin ディレクトリーに常駐する、簡易機能の Web サーバーを作成する方法です。そのため、完全な Android カーネルのソース・コードと、これをビルドするための GNU ツールをダウンロードしてインストールすることが重要となります。Android カーネルのオープンソース・プロジェクトのホーム・ページ (「参考文献」を参照) に、repo という名前のスクリプトでプラットフォーム全体をダウンロードする手順が簡潔に記載されています。

この小さなクラウド・ソフトウェアは、ワークステーション上で ARM プラットフォームにクロスコンパイルすることになるので、そのために必要なすべてのツールを確実にインストールしてください。リスト 1 に示すように apt-get を使用すれば、必要なツールがすべてインストールされます。

リスト 1. クロスコンパイル・ツールのインストール
    $ sudo apt-get install git-core gnupg \
    sun-java5-jdk flex bison gperf libsdl-dev \
    libesd0-dev libwxgtk2.6-dev build-essential \
    zip curl libncurses5-dev zlib1g-dev

開発用のディレクトリー構造

Android カーネル・ソースのインストール・ディレクトリーには、mydroid という名前のディレクトリーを使用します。この名前のディレクトリーが作業の開始点となる事実上のルート・ディレクトリーとなるので、まず、ホーム・ディレクトリーに移動して mkdir mydroid コマンドを実行します。次に cd mydroid を実行してこのディレクトリーに移動し、repo sync コマンドを実行します。

repo sync コマンドによって Android ソース・コードのすべてが mydriod ディレクトリーにダウンロードされると、以下を始めとする注目に値するサブディレクトリーがいくつか作成されます。

  • mydroid/external ディレクトリーには、cloud ディレクトリーを作成することになります。ここに、このプロジェクトのソース・コード (cloud.c) が配置されます。
  • 長時間実行される make コマンドによって Android システムが構築されると、out/target/product/generic/system/bin ディレクトリー内にクラウドのバイナリー・ファイルが作成されます。

Android クラウド・プロジェクト

クラウド・プログラムは開始されると同時に、このプログラムに渡されたコマンドライン・パラメーターがないかどうかをチェックします。オプションで想定される 2 つのパラメーターは、モニター対象のポートと、開始点となるホーム・ディレクトリーです。このパラメーターがどちらも指定されていない場合、プログラムはデフォルトで標準ポート 80 を使用し、デフォルトのホーム・ディレクトリーとしては、プログラムの開始時にカレント作業ディレクトリーであったホーム・ディレクトリーを設定します。

起動が完了すると、プログラムは前述のポートを介した呼び出しを「リッスン」するように TCP/IP ソケットを初期化した後、デーモン・プロセスとしてブラウザーからの呼び出しを待機し、呼び出しに対応するようになります。ブラウザーがこの小さなクラウド・サーバーのデフォルトの「ページ」を呼び出すと、コードは前述の「ホーム」ディレクトリーのディレクトリー・リストを返すことによって応答します。ファイル名にハイパーリンクが付けられるかどうかは、それが既知のファイル・タイプであるかどうか、あるいはディレクトリーであるかどうかによって決まります。既知のファイル・タイプとは、(WWW の世界では) そのファイルに対応する MIME タイプがあるということを意味します。例えば Android 携帯電話の内部では、着信音は .ogg ファイルとして保管されています。したがって audio/ogg というMIME タイプはブラウザーに対し、そのブラウザーが適切に .ogg ファイルを再生できるように構成されている前提の下、ファイルをスピーカーで再生するように指示します。

ファイル名のハイパーリンクとは別にリストの先頭に表示されるハイパーリンクは、親ディレクトリーです。このリンクをクリックすると、カレント・ディレクトリーの親ディレクトリーに移動することができます。この操作を繰り返すと、ファイルシステム階層の最上位ディレクトリーであるルート・ディレクトリーまで移動することができます。移動した先では、そのディレクトリーのサブディレクトリーの名前がハイパーリンクとして表示されます。このハイパーリンクをクリックすることで、該当するサブディレクトリーに移動して、そこに含まれるファイルを調べることができます。

このように、このクラウド・アプリケーションは携帯電話のファイルシステムを容易に探ることができる便利なプログラムです。さらに、ソース・コード (「ダウンロード」を参照) を機能のテンプレートとして利用して、必要に合わせてコードを自由に変更することもできます。コードの変更方法については、この記事の最後に提案します (ご使用のワークステーションでコードをコンパイルして実行し、ワークステーションのファイルシステムも参照可能にすることができます。その方法については、「参考文献」を参照してください)。


C ソース・コード

どのプログラムのソース・コードにしても、少なくともそのプログラムの名前と作成者は明記しなければなりません。リスト 2 に、属性、定義を引用するインクルード・ファイル、そしていくつかの有用な定数を記載します。

リスト 2. 属性、インクルード・ファイル、有用な定数
    // Android Cloud Application by Bill Zimmerly.
    // Based on "NWEB" by Nigel Griffiths.

    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <fcntl.h>
    #include <signal.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>

    #define TRUE      -1
    #define FALSE     0
    #define SBUF      1048576
    #define LBUF      4096
    #define LAPN      64
    #define ERROR     1
    #define LOG       2
    #define LOGGING   FALSE

リスト 3 に、このプログラムに定義されたすべての関数がアクセスできるグローバル作業ストレージを記載します。バッファー・ポインターのほとんどは、ブラウザーが小さなクラウドを呼び出すときに割り当てられるストレージです。コンテンツがブラウザーに返された後、割り当てられたメモリーは携帯電話に返されます。このプラクティスは、リソースが限られているコンピューターでは他のどの検討事項にも増して重要になってきます。

リスト 3. グローバル作業ストレージ
    char* about="</pre>Cloud is a simple application that enables "
                "web-based browsing of the Android file system "
                "and is intended for use by people who like to "
                "explore the lowest levels of their device. It "
                "runs on a rooted Android phone and is only for "
                "browsing the file system. See the IBM "
                "developerWorks article for a full description "
                "of this application.<pre>";

    char* mainbuf;
    char* theDir;
    char* thePort[8];
    char* theList;
    char* fstr;

    char logDir[LBUF];

    int ret;

このプログラムでは単純な表を使ってファイル拡張子を MIME タイプに対応させることで、ブラウザーが MIME タイプを把握してそのファイルの中身を適切に扱えるようにします。リスト 4 の構造体は、mimeokay 関数が 1 つの目的のために使用します。その目的とは、グローバル文字列ポインター fstr が、ファイル拡張子 fext と一致する適切な MIME タイプ文字列を指すように設定することです。この情報によって、小さなクラウドはファイル名にハイパーリンクを付けて表示するか、あるいはリンクなしのプレーン・テキストとして表示するかを判断することができます。また、ファイルの中身がブラウザーに送信されるときに MIME タイプを指定するという二重の役割も果たします。

リスト 4. MIME タイプの判別
    struct
    {
      char *ext;
      char *mimetype;
    }

    mimes [] = 
    {
      {".htm",  "text/html"  },  
      {".html", "text/html"  },
      {".xml",  "text/xml"   },
      {".gif",  "image/gif"  },  
      {".jpg",  "image/jpeg" }, 
      {".jpeg", "image/jpeg" },
      {".png",  "image/png"  },  
      {".log",  "text/plain" },
      {".conf", "text/plain" },
      {".rc",   "text/plain" },
      {".sh",   "text/plain" },
      {".prop", "text/plain" },
      {".txt",  "text/plain" },
      {".TXT",  "text/plain" },
      {".cpp",  "text/plain" },  
      {".c",    "text/plain" },  
      {".h",    "text/plain" },
      {".ogg",  "audio/ogg" },
      {0,0} 
    };

    void mimeokay(char* fext)
    {
      int buflen;
      int len;
      long i;

      buflen=strlen(fext);
      fstr = (char*) 0;
  
      for(i=0; mimes[i].ext != 0; i++)
      {
        len = strlen(mimes[i].ext);
    
        if(!strncmp(&fext[buflen-len], mimes[i].ext, len))
        {
          fstr=mimes[i].mimetype;
          break;
        }
      }
    }

リスト 5 に記載するのは、開発およびテスト中に Android クラウドのアクティビティーを cloud.log ファイルにログとして記録するオプションのコードです。クラウドは比較的速度の遅いハードウェアで稼働することから、クラウドの変更中に何が起こっているのかコードを確認する必要がない限り、この機能は無効にしておいたほうが無難です。リスト 1 に記載した LOGGING 定数は、このロギング機能を有効にする場合には TRUE にしなければなりませんが、この機能によるパフォーマンス・ヒットはかなり大きいので、携帯電話上でクラウドを実行するときは FALSE のままにしておいてください。

リスト 5. ロギング・アクティビティー
    void aclog(int type, char *s1, char *s2, int num)
    {
      int fd ;
      char aclogbuffer[LBUF];
      char logFile[LBUF];
    
      if(!LOGGING)
        return;
    
      switch(type)
      {
        case ERROR: 
          sprintf(aclogbuffer, "ERROR: %s:%s Error Number=%d, PID=%d", 
                  s1, s2,
                  errno, getpid());
          break;
        case LOG:
          sprintf(aclogbuffer, "INFO: %s:%s:%d", s1, s2, num);
          break;
      }	
    
      strcpy(logFile, logDir);
      strcat(logFile, "/cloud.log");
      
      if((fd = open(logFile, O_CREAT | O_WRONLY | O_APPEND,
                    0644)) >= 0)
      {
        ret=write(fd, aclogbuffer, strlen(aclogbuffer)); 
        ret=write(fd, "\n", 1);      
        close(fd);
      }
    
      if(type == ERROR)
        exit(3);
    }

リスト 6 に、小さなクラウドの HTML 出力を作成する目的で定義された一連の関数を記載します。適切に設計された Web サイトでは、すべてのページで共通の外観になっていなければなりません。この全体の外観を定義する場所は、buildbuf 関数です。

リスト 6. HTML 出力の生成
    void apname(void)
    {
      strcat(mainbuf, "Android Cloud Application");
    }
    
    void buildbuf(char* data)
    {
      mainbuf[0]=0;
      strcat(mainbuf, "<html>\n<head>\n<title>");
      apname();
      strcat(mainbuf, "</title>\n</head>\n");  
      strcat(mainbuf, "<body>\n<h3>");
      apname();
      strcat(mainbuf, "</h3>\n");
      strcat(mainbuf, "<a href=\"/About_\">About</a><br>\n");
      strcat(mainbuf, "<a href=\"/Home_\">Home</a><br>\n");
      strcat(mainbuf, "<hr>\n");
      strcat(mainbuf, "Dir: ");
      strcat(mainbuf, theDir);
      strcat(mainbuf, "<br>\n<hr>\n<pre>\n");
      strcat(mainbuf, data);
      strcat(mainbuf, "\n</pre>\n");
      strcat(mainbuf, "</body>\n</html>\n");
    }
    
    void htmlout(int fd, char* data)
    {
      fstr=mimes[0].mimetype;
      sprintf(mainbuf, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
      ret=write(fd, mainbuf, strlen(mainbuf));
      buildbuf(data);
      ret=write(fd, mainbuf, strlen(mainbuf));
    }
    
    void error404(int fd)
    {
      fstr=mimes[0].mimetype;
      sprintf(mainbuf, "HTTP/1.0 404 OK\r\nContent-Type: %s\r\n\r\n", fstr);
      ret=write(fd, mainbuf, strlen(mainbuf));
      buildbuf("404 Error - File not found!");
      ret=write(fd, mainbuf, strlen(mainbuf));
    }

リスト 7 を見ると、この小さなクラウドがどのようにして、MIME タイプが定義されたファイルの中身を返すかがわかります。ブラウザーでは、MIME タイプは Content-Type: に続くストリングとしてサーバーから送信されると想定していればよいことに注目してください。クラウドによって生成されたリンクをクリックするだけで、この関数が呼び出され、該当するファイルの中身が返されます。ただし、創造力に富んだいたずら好きが自分のブラウザーのアドレス・バーに不適切なファイル名を入力したとすると、error404 関数 (上記で定義した関数) がユーザーにエラーを通知します。

リスト 7. ファイルの中身を返す方法
    void retfile(int fd, int hit)
    {
      int file_fd;
      long ret;
    
      mimeokay(mainbuf);
    
      if(fstr == 0)
      {
        error404(fd);
        return;
      }
    
      if((file_fd = open(&mainbuf[4], O_RDONLY)) == -1)
      {
        error404(fd);
      }
      else
      {  
        aclog(LOG, "SEND", &mainbuf[4], hit);
    
        sprintf(mainbuf, "HTTP/1.0 200 OK\r\nContent-Type: %s\r\n\r\n", fstr);
        ret=write(fd, mainbuf, strlen(mainbuf));
    
        while((ret=read(file_fd, mainbuf, SBUF)) > 0 )
        {
          ret=write(fd, mainbuf, ret);
        }
      }
    }

リスト 8 に、この小さなクラウドがハイパーリンク付きのメイン・ファイル・リストを作成する方法を示します。hyper 関数は何度も使用されるため、関数として独立させるのが賢明です。isDir 値がファイル名のフル・パスの前に /CD_ というテキストを追加するため、小さなクラウドは、そのディレクトリーの内容を表示しなければならないことを認識します。fileList 関数は、まさにこのアプリケーションの核心です。

リスト 8. ディレクトリーの内容を表示する関数
    void hyper(int isDir, char* name)
    {
      strcat(theList, "<a href=\"");
    
      if(isDir)
        strcat(theList, "/CD_");
    
      strcat(theList, (char*) theDir);
    
      if(strcmp(theDir, "/"))
        strcat(theList, "/");
    
      strcat(theList, name);
      strcat(theList, "\">");
      strcat(theList, name);
      strcat(theList, "</a>");
      strcat(theList, "\n");
    }

    char* fileList(void)
    {
      struct dirent **namelist;
      int n;
      long i;
      long j;
    
      theList[0]=0;
    
      n=scandir(".", &namelist, 0, (void*) alphasort);
    
      if (n < 0)
        perror("scandir");
      else
      {
        for(i=0; i<n; i++)
        {
          if(namelist[i]->d_type == DT_DIR)
          {
            if(!strcmp(namelist[i]->d_name, "."))
            {
              // strcat(theList, namelist[i]->d_name);
            }
            else if(!strcmp(namelist[i]->d_name, ".."))
            {
              if(strcmp(theDir, "/"))
              {
                strcat(theList, "<a href=\"");
                strcat(theList, "/CD_");
                strcat(theList, (char*) theDir);
    
                j=strlen(theList);
    
                while(j--)
                {
                  if(theList[j] == '/')
                  {
                    theList[j]=0;
                    j=1;
                  }
                }
                
                if(!strcmp(&theList[strlen(theList)-4], "/CD_"))
                {
                  strcat(theList, "/");
                }
    
                strcat(theList, "\">Parent Directory</a>");          
                strcat(theList, "\n");
              }
            }
            else
              hyper(TRUE, namelist[i]->d_name);
          }
          else
          {
            mimeokay(namelist[i]->d_name);
        
            if(fstr == 0)
            {
              strcat(theList, namelist[i]->d_name);
              strcat(theList, "\n");
            }
            else
              hyper(FALSE, namelist[i]->d_name);
          }
    
          free(namelist[i]);
        }  
    
        free(namelist);
      }
      
      return theList;
    }

リスト 9 では、小さなクラウド・サーバーの child 関数の機能を定義しています。この機能はサーバーがブラウザーのリクエストを受け取るたびに実行されます。その実行内容は単純で、リクエストに対応するために必要なバッファーを割り当て、バッファーを処理した後で解放することで、システム・メモリーが必要でないときにはその割り当てを少なく維持するというものです。携帯電話では、メモリーは限られているだけに貴重なリソースです。そのため、プログラムがリクエストに対応し終わったら、メモリーを解放してシステムに返さなければなりません。

リスト 9. デーモン child 機能
    void child(int fd, int hit)
    {
      long i;
      long ret;
      char* cret;
    
      mainbuf=malloc(SBUF+1);
      theList=malloc(SBUF+1);
      theDir=malloc(LBUF+1);
      cret=getcwd(theDir, LBUF);
      
      ret=read(fd, mainbuf, SBUF);
    
      if(ret == 0 || ret == -1)
      {
        error404(fd);
      }
      else
      {
        if(ret > 0 && ret < SBUF)
          mainbuf[ret]=0;
        else
          mainbuf[0]=0;
    
        for(i=0; i<ret; i++)
          if(mainbuf[i] == '\r' || mainbuf[i] == '\n')
            mainbuf[i]='*';
    
        aclog(LOG, "request", mainbuf, hit);
    
        for(i=4; i < SBUF; i++)
        {
          if(mainbuf[i] == ' ')
          {
            mainbuf[i]=0;
            break;
          }
        }
    
        if(!strncmp(&mainbuf[0], "GET /\0", 6) ||
           !strncmp(&mainbuf[0], "get /\0", 6))
        {
          htmlout(fd, fileList());
        }
        else
        {
          if(!strncmp(&mainbuf[5], "About_", 6))
          {
            htmlout(fd, about);
          }
          else if(!strncmp(&mainbuf[5], "Home_", 5))
          {
            htmlout(fd, fileList());
          }    
          else if(!strncmp(&mainbuf[5], "CD_", 3))
          {
            if(chdir(&mainbuf[8]) == -1)
            {
              error404(fd);
            }
            else
            {
              if(strcmp(theDir, &mainbuf[8]))
                strcpy(theDir, &mainbuf[8]);
                
              htmlout(fd, fileList());
            }
          }
          else
          {
            retfile(fd, hit);
          }
        }
      }
    
      free(theDir);
      free(theList);  
      free(mainbuf);  
      sleep(1);
      exit(1);
    }

リスト 10 では、小さなクラウドの main (親) 関数の機能を定義しています。この関数は、ブラウザーのリクエスト呼び出しをリッスンする場所となる TCP/IP ソケットを割り当て、theDir (小さなクラウドが開始された場所) などのグローバル変数を初期化します。そして最後に、(デーモンとして知られる) 常駐プログラムの形にし、他のプロセスが実行している間、ブラウザーのリクエストをバックグラウンドで静かに処理します。

リスト 10. main 関数の機能
    int main(int argc, char **argv)
    {
      char* str;
      char* cret;
    
      static struct sockaddr_in cli_addr;
      static struct sockaddr_in serv_addr;
    
      socklen_t length;
    
      int i;
      int port;
      int pid;
      int listenfd;
      int socketfd;
      int hit;
    
      cret=getcwd(logDir, LBUF);
    
      if(argc < 2)
      {
        strcpy((char*) thePort, "80");
        port=atoi((char*) thePort);
      }
      else
      {
        if(!strcmp(argv[1], "-?"))
        {
          printf("Usage: cloud [Port Directory]\n");
          exit(0);
        }
        strcpy((char*) thePort, argv[1]);
        port=atoi((char*) thePort);
    
        if(port < 0 || port > 60000)
          aclog(ERROR, "Invalid port number (try 1 --> 60000)", argv[1], 0);
    
        if(chdir(argv[2]) == -1)
        {
          printf("ERROR: Invalid directory %s\n", argv[2]);
          exit(4);
        }
      }
    
      if(fork() != 0)
        return 0;
    
      signal(SIGCHLD, SIG_IGN);
      signal(SIGHUP, SIG_IGN);
    	
      for(i=0; i<32; i++)
        close(i);
    	
      setpgrp();
    
      aclog(LOG, "Cloud Port/PID=", (char*) thePort, getpid());
        
      if((listenfd=socket(AF_INET, SOCK_STREAM, 0)) < 0)
        aclog(ERROR, "syscall", "socket", 0);
    	
      serv_addr.sin_family      = AF_INET;
      serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
      serv_addr.sin_port        = htons(port);
    
      if(bind(listenfd, (struct sockaddr*) &serv_addr,
              sizeof(serv_addr)) < 0)
        aclog(ERROR, "syscall", "bind", 0);
    
      if(listen(listenfd, 64) <0)
        aclog(ERROR, "syscall", "listen", 0);
    
      for(hit=1; ;hit++)
      {
        length=sizeof(cli_addr);
        
        if((socketfd=accept(listenfd,
                            (struct sockaddr*) &cli_addr,
                            (socklen_t*) &length)) < 0)
          aclog(ERROR, "syscall", "accept", 0);
    
        if((pid=fork()) < 0)
        {
          aclog(ERROR, "syscall", "fork", 0);
        }
        else
        {
          if(pid == 0)
          {
            close(listenfd);
            child(socketfd, hit);
          }
          else
          {
            close(socketfd);
          }
        }
      }
    }

アプリケーションのコンパイル、デプロイメント、テスト

クラウド・アプリケーションを適切にビルドするには、Android カーネル・ソースの makefile を使用して、もう 1 つファイルを作成しなければなりません。Android.mk という名前のファイルを作成し、このファイルにリスト 11 の内容を貼り付けてください (このファイルは、「ダウンロード」で入手できるソースにも含まれています)。

リスト 11. Android.mk
    ifneq ($(TARGET_SIMULATOR),true)
    
    LOCAL_PATH:= $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_SRC_FILES:= cloud.c
    LOCAL_MODULE := cloud
    LOCAL_STATIC_LIBRARIES := libcutils libc
    include $(BUILD_EXECUTABLE)
    
    endif  # TARGET_SIMULATOR != true

Android カーネル・ソースの外部ディレクトリーにナビゲートし、cloud という名前のサブディレクトリーを作成してください。このディレクトリーに、cloud.c ファイルと Android.mk ファイルの両方を入れます。これで、小さなクラウド・アプリケーションが含まれる新しい Android カーネル・システムをビルドする準備が整いました。

Android カーネル・ソースのルート・ディレクトリーに移動し、make と入力したら、コーヒーでも飲みに行ってください。このプロセスには時間がかかるため、ここはシステムに任せて、そのジョブを完了させるしかありません。

万事順調に進んでシステムが make コマンドの実行を完了すると、小さなクラウドのバイナリー・ファイルが out/target/product/generic/system/bin ディレクトリー内に作成されます。わざわざディストリビューションをまるごとすべて、ルート化された Android 携帯電話にインストールする必要はありません。クラウドのバイナリーを SD カードにコピーするだけで十分です。リスト 12 に、ホスト・コンピューターでバイナリーをコピーする方法を示します (mydroid ディレクトリーに Android ソース・カーネル・コードがすべて含まれていることが前提です)。

リスト 12. カーネルおよびクラウドの作成
    $ cd mydroid/external
    $ mkdir cloud
    $ cd cloud
    $ cp ~/src/tinycloud/cloud.c .
    $ cp ~/src/tinycloud/Android.mk .
    $ cd ../..
    $ make
    
    --- Android system "make" messages scroll up the screen for a long time. ---
    
    $ cd out/target/product/generic/system/bin
    $ cp cloud /media/ANDROIDSDCARD/.

/media/ANDROIDSDCARD は、携帯電話が接続されてマウントされていることが前提となることに注意してください。また、お使いの SD カードの名前は異なっている場合があるので、/media サブディレクトリー (Ubuntu Linux で実行している場合) で正しい名前を調べてください。

Android Market に用意された無料で入手できるターミナル・プログラムのいずれかを実行すれば、Android 携帯電話自体でシェル・セッションを実行することができます。Android クラウドは system/bin ディレクトリーに常駐するのが通常ですが、これは必須ではありません。テストの目的で、別のディレクトリーに置いて実験してみてください。それには /data の下に、cloud という名前のディレクトリーを作成し、クラウドのバイナリーを /sdcard ディレクトリーから /data/cloud にコピーします。その上で、chmod コマンドを実行してクラウド・プログラムを実行可能にし、cloud と入力してこのプログラムを実行します。このステップをコードにすると、リスト 13 のようになります。

リスト 13. Android 携帯電話でのクラウドのテスト
    $ su
    # cd data
    # mkdir cloud
    # cd cloud
    # cp /sdcard/cloud .
    # chmod 777 cloud
    # ./cloud
    # ps
    
    --- Listing of resident daemons, and "./cloud" should be one of them. ---

Home キーを押してブラウザーを起動し、URL http://localhost にアクセスします。すると Android のブラウザーに、小さなクラウドの出力が表示されるはずです。ここに表示されたリンクをクリックすれば、携帯電話のファイルシステムのなかをあちこち移動することができます。

次に、Android 携帯電話の Wi-Fi がネットワーク上で動作している場合、コンピューターを使って、この小さなクラウドを呼び出すことができます。それには、携帯電話のWi-Fi IP アドレスがわかっていなければなりません。アドレスを判断する方法はさまざまにあります。例えば、Wi-Fi ルーターのログを調べるなどです。あるいはターミナル・プログラムに戻ってnetstat -r と入力すると、リスト 14 のようなエントリーが表示されます。

リスト 14. netstat による携帯電話の IP アドレスの取得
    # netstat -r
    Proto Recv-Q Send-Q  Local Address    Foreign Address    ... 
    .
    .
    .
    tcp        0      0  192.168.0.3:80   192.168.0.5:58744  ...
    .
    .
    .

ブラウザーのアドレス・バーに http://192.168.0.3/ (お使いの携帯電話の IP アドレス) と入力してください。すぐさま、小さなクラウドによって提示された Android 携帯電話のファイルシステム・リストの中身を覗けるようになるはずです。

携帯電話のなかの小さなクラウドを使用すると同時に、作成中の Web ページを参照するには、Home キーを押しながらブラウザーまたはエディターをクリックして、使用中のアプリケーションを切り替えればよいだけです。ブラウザーをクリックする場合は、メニューの表示を最新の状態にして、編集された変更内容を表示します。望み通りの表示になるまで編集とテストのサイクルを繰り返すことによって、歯科医院で順番を待っている間に Web をマスターすることができます。


プロジェクトの拡張方法

プロジェクトを拡張する方法は、アイデアを持っている人の数だけありますが、ここでは皆さんが試してみたいと思うような方法をいくつか提案します。

  • Java という名前の具体的なメニュー項目を追加します。このメニュー項目を選択することによって Java クラス・ファイルが置かれたディレクトリーに自動的にナビゲートされ、これらのファイルをクリックすることで、その内容を調べられるようにします (クラス・ファイルは実際には圧縮されたディレクトリーなので、ハイパーリンクの対象になります)。この拡張は、興味の対象となるコードが含まれる他のどのディレクトリーにも適用することができます。
  • トップ・ページのようなページを追加し、例えば毎分このページが自動的に更新されて、/proc ディレクトリから情報を読み込むことができる選択肢を表示するようにします。
  • Web ページから SQLite データベースにクエリーを実行できるようにするためのコードを作成します。
  • SD カードには、HTML ベースのプレゼンテーションをまるごと収容することができます。さらに、携帯電話の移植性は優れているため、最近のプレゼンテーション実行環境に組み込まれた Web ブラウザーを使用することによって、スライド・ショーによるプレゼンテーションを携帯電話で表示することができます。

この小さなクラウドを改善するためのアイデアが決定したら、以下の一般的な 3 つのステップに従って、該当する機能を追加してください。

  1. 新しく追加する機能を表す新規メニュー項目を入力して、リスト 6buildbuf 関数を変更します。
  2. 新規機能に対応できるようにリスト 9child 関数を変更します。例えば、About_ メニュー項目が buildbuf 関数と child 関数で機能する仕組みを調べれば、新しい機能をいかに簡単に、この小さなクラウドに追加できるかがわかります。
  3. メニュー項目に対処する関数を作成します。そして必ず、この新しい関数の呼び出しを child 関数に組み込んでください。

まとめ

使用するデバイスについて可能な限りの知識を得ようとすることは、常に正しい態度です。知識を得ることに加え、知識を得るためのツールを作成する作業は大いに楽しいものです。小さなクラウド・コンピューティング・アプリケーションを Android 携帯電話の中心に組み込むことは面白くもあり、そしてためになる作業でもあります。この作業によって、これらの驚くべきデバイスの内部が実際にどうなっているのかを否が応でも沢山学ぶことになるからです。


ダウンロード

内容ファイル名サイズ
Source code for the tiny cloud appos-tinycloud-source.zip9KB

参考文献

学ぶために

製品や技術を入手するために

  • 公式サイトに Android アクセスして、ダウンロードおよび詳細情報を入手してください。
  • Android カーネルのソース・コードを入手するには git リポジトリーにアクセスしてください。
  • CodeSourcery から Sourcery G++ Lite パッケージを入手してください。
  • IBM ソフトウェアの試用版を使用して、次のオープンソース開発プロジェクトを革新してください。ダウンロード、あるいは DVD で入手できます。
  • IBM 製品の評価版をダウンロードするか、あるいは IBM SOA Sandbox のオンライン試用版で、DB2®、Lotus®、Rational®、Tivoli®、および WebSphere® などが提供するアプリケーション開発ツールやミドルウェア製品を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Open source, Cloud computing
ArticleID=455812
ArticleTitle=Android のなかの小さなクラウド
publish-date=11172009