真実の楽譜(フルスコア)

プログラム関係の忘備録になるはず

AndroidはKitKat以降アプリからSDカードへの書き込み不可に?

2014/01/30 18:30 内容一部修正

KitKat端末では今後アプリからMicro SDカードへの書き込みができくなる模様 | juggly.cn
この記事もうちょっと詳しく調べてみた。

情報元ソース

元ソースはGoogle+Google社のTod Liebeck氏書いた内容

KitKat will make your SD Card completely useless: per the Android API specification, apps can no longer write files to your SD card. And Samsung is following it.

This only applies to dual-storage devices, i.e., devices with a user-writable internal flash storage AND a removable SD card.


From http://source.android.com/devices/tech/storage/index.html:

"The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions."


If your device has user-accessible internal flash storage, your SD Card is a "secondary external storage device".


What this means is that with KitKat, applications will no longer be able create, modify, or remove files and folders on your external SD card. As a for-instance, you can no longer use a file manager to copy files from your computer to the SD card over a network. This ability, which has existed since the beginning of Android, has been taken away.


The only stated reason for this removal of functionality is that, "Restricting writes in this way ensures the system can clean up files when applications are uninstalled." I do not pretend to understand this logic. Apps are still allowed to write in arbitrary directories on the primary storage (with the appropriate permission), but are denied the same access to external storage.


Samsung has implemented this feature with their KitKat OTA updates. Note3 users are now complaining that FX File Explorer can no longer write to their external SD cards. There are solutions to this problem for users with root access. Users without root access appear to be screwed.

I'm not quite certain how Google intends for you to place files on your SD card. Perhaps you have to use proprietary Google apps that contain permissions unavailable to the rest of the developer world. Perhaps you're supposed to put everything on the cloud and pay carrier data fees to get it there. Perhaps you're supposed to use some kind of WIRE to attach your WIRELESS device to your computer and have the computer do that work for you.


In my opinion this is a horrible misstep by Google and the Android Open Source Project. Functionality has been removed without reason, to the severe detriment of users and developers alike.


I apologize for not bringing this to everyone's attention when KitKat 4.4 was released, but it was not mentioned in the Android 4.4 changes document: http://developer.android.com/about/versions/android-4.4.html. It's only mentioned in the article on source.android.com. I was only made aware of its existence from user reports as a result of Samsung implementing this change in its KitKat OTA updates.

https://plus.google.com/+TodLiebeck/posts/gjnmuaDM8sn

要約

翻訳ミスがあるかもしれないので注意してね。

KitKatから仕様変更
  • アプリはSDカードへの書き込みが完全にできなくなる
  • (例)ファイルマネージャーアプリでファイルをSDカードに保存できなくなる
  • サムスンはGALAXY Note3のアップデートで追従した
デュアルストレージデバイスが対象
  • 対象はフラッシュストレージと取り外し可能なSDカードを持つデバイス
  • WRITE_EXTERNAL_STORAGEパーミッションでは、主要なストレージのパッケージ固有ディレクトリにしか書き込めない
  • WRITE_EXTERNAL_STORAGEパーミッションがあれば主要なストレージ内のどこにでも書き込みが可能 (2014/01/30 18:30 修正)
  • SDカードは2番目の外部ストレージになり書込みが許可されない
  • SDカードは2番目の外部ストレージになりパッケージ固有ディレクトリにしか書込みが許可されない (2014/01/30 18:30 修正)
  • root権限がないと回避できない
仕様決定理由
  • 書込みを制限させ、アプリのアンインストール時に完全にクリーンアップできるから
Tod Liebeck氏は不満
  • 今後Googleがどうしたいかはわからない
  • でも代価としてクラウドサービスとかを用意して金取るかも
  • オープンソースプロジェクトとして失策
  • 変更内容に書かれてなくてサムスンのアップデートで知ったごめん

修正について

完全に書き込みができないのではなく、
SD内のアプリケーションのパッケージ固有ディレクトリ[SD\Android\data\パッケージ名\]
なら保存できるってのが正確な仕様のようです。

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

External Storage Technical Information | Android Developers

Tod Liebeck氏が

KitKat will make your SD Card completely useless

KitKatではSDカードが完全に使えなくなる」
って書いていて矛盾してるのは記述ミスなんだろうか?

ILSpyを使ってC#プログラムをリバースエンジニアリング

C#の実行プログラムやDLLはそのままだと、
リバースエンジニアリングし放題ってことの確認。

シーザー暗号プログラムテスト

今回はテストとして、入力文字列をシーザー暗号で
暗号化と復号化を行うプログラムを作成しました。
その後リバースエンジニアリングで、
プログラムがどう見えるのか確認してみます。

シーザー暗号とは

シーザー暗号(シーザーあんごう、英語:Caesar cipher)は、単一換字式暗号の一種で、平文の各文字を辞書順に3文字だけシフトして暗号文をつくる暗号である。カエサル暗号とも呼ばれる。文字のシフト数は固定であるが、3に限る必要はなく、シフト数を3以外にした方式もシーザー暗号(あるいはシフト暗号)と呼ぶことがある。

参考:シーザー暗号 - Wikipedia

シーザー暗号プログラム

CaesarTest.cs
using System;

namespace Encryption
{
  static class CaesarTest
  {
    private static int rool = 3;

    private static string Encryption(string input)
    {
      string ret = "";
      foreach (char c in input)
      {
        ret += ((char)(c + rool)).ToString();
      }
      return ret;
    }

    private static string Decryption(string input)
    {
      string ret = "";
      foreach (char c in input)
      {
        ret += ((char)(c - rool)).ToString();
      }
      return ret;
    }

    public static void Main()
    {
      string input;
      string encrypted;
      string decrypted;

      //復号化
      Console.WriteLine("入力した文字を暗号化します。");
      input = Console.ReadLine();
      encrypted = Encryption(input);
      Console.WriteLine("暗号化データ:" + encrypted);

      //復号化
      Console.WriteLine("入力した文字を復号化します。");
      input = Console.ReadLine();
      decrypted = Decryption(input);
      Console.WriteLine("復号化データ:" + decrypted);

      Console.ReadLine();
    }
  }
}
実行例

入力した文字を暗号化します。
password
暗号化データ:ufxx|twi
入力した文字を復号化します。
ufxx|twi
復号化データ:password

Encryption(暗号化)で「password」が「sdvvzrug」に、
Decryption(復号化)で「sdvvzrug」が「password」として出力されました。

ILSpyでリバースエンジニアリング

ILSpyのダウンロード

http://ilspy.netから画面上部のDownload Binariesを選択し、
ILSpyをダウンロード。

2013/01/29時点 最新 Version 2.1.0.1603

ILSpyの実行

ダウンロードしたファイルを解凍し、
「ILSpy.exe」を起動。

メニューバーの[File]→[開く]から、
今回作成したプログラムの実行ファイルを選択。

左側のツリービューに追加された
「Encryption」を展開していくと…

f:id:s_sikisya:20140129201156p:plain

ソースの中身がほぼ丸見え!
このままではアルゴリズムがバレバレで暗号化の意味が無い。

三角形ボタンの作り方(C# Form)

C# Formアプリケーションで、
上下左右や、進む・戻る等を指定するときに便利な三角形ボタンの作り方。

雑なやり方(非推奨)

標準のボタンをFormに貼り付けて、
テキストに三角形の記号文字を指定。

public Form1()
{
  InitializeComponent();
  button1.Text = "▲";
}

しかしこれだと右向きや左向きの三角形の場合特殊文字を使うため、
表示されない可能性もあります。

ボタン内に三角形を描画するやり方

System.Windows.Forms.Buttonクラスを継承し、
ボタン内に三角形を描画するように拡張を行います。

Button継承クラスの作成

Buttonクラスを継承した以下のクラスを作成します。

ButtonBGTriangle.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;

namespace ButtonTriangleTest
{
  public partial class ButtonBGTriangle : System.Windows.Forms.Button
  {
    //三角形の方向指定用列挙体
    public enum TriangleDirection
    {
      Up = 1,
      Down = 2,
      Left = 4,
      Right = 8,
    }

    //三角形の方向指定プロパティ
    private TriangleDirection TRIANGLE_DIRECTION = TriangleDirection.Left;
    public TriangleDirection Direction
    {
      get { return this.TRIANGLE_DIRECTION; }
      set
      {
        this.TRIANGLE_DIRECTION = value;
        this.Invalidate();
      }
    }

    //三角形の塗りつぶし色プロパティ
    private Color TRIANGLE_COLOR = System.Drawing.SystemColors.ControlText;
    public Color TriangleColor
    {
      get { return this.TRIANGLE_COLOR; }
      set
      {
        this.TRIANGLE_COLOR = value;
        this.Invalidate();
      }
    }

    public ButtonBGTriangle()
    {
      this.Text = "";
    }

    protected override void OnTextChanged(EventArgs e)
    {
      base.OnTextChanged(e);
      //テキストを空に変更し無効化
      this.Text = "";
    }

    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
    {
      base.OnPaint(e);

      Graphics g = e.Graphics;
      g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
      PointF[] drawPoint = new PointF[3];

      //TRIANGLE_DIRECTIONに応じて三角形を描画
      switch (TRIANGLE_DIRECTION)
      {
        case TriangleDirection.Up:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 2.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 6.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 5.0f / 6.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Down:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 2.0f, (this.ClientSize.Height - 1.0f) * 3.0f / 4.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 6.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 5.0f / 6.0f, (this.ClientSize.Height - 1.0f) / 4.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Left:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) / 6.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) * 5.0f / 6.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) / 2.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
        case TriangleDirection.Right:
          drawPoint[0] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) / 6.0f);
          drawPoint[1] = new PointF((this.ClientSize.Width - 1.0f) / 4.0f, (this.ClientSize.Height - 1.0f) * 5.0f / 6.0f);
          drawPoint[2] = new PointF((this.ClientSize.Width - 1.0f) * 3.0f / 4.0f, (this.ClientSize.Height - 1.0f) / 2.0f);
          g.FillPolygon(new SolidBrush(TRIANGLE_COLOR), drawPoint);
          break;
      }
    }
  }
}

これを作成すると、
こんな感じ↓のボタンコンポーネントが作れます。

f:id:s_sikisya:20140129114952p:plain

プロパティ

三角形の方向と塗りつぶし色はプロパティで設定します。

Direction:三角形の向き(Up,Down,Left,Right)
TriangleColor:三角形のカラー

Scilabで離散ウェーブレット変換

最近ウェーブレット変換を勉強していますが、
雰囲気が掴みきれていないので実際にやってみることにした。
前回書いたライブラリ実装は理解や実装に時間がかかりそうなので、
別の方法としてScilabのWavelet Toolboxを使ったら結構楽にできました。
今回使用したOSはWindows 7 64BitですがMacUnixでも動く模様。

Scilabとは

Scilabは、 INRIA (フランス国立 コンピュータ科学・制御研究所)と ENPC で 開発された高機能な科学技術ソフトウエアです。 2003年にScilab の開発は Scilab コンソーシアムに移管され、 現在は更にDigiteoに統合されて、 その1部門として Scilab コンソーシアムの活動を行っています。 2008年以降、Scilabは、GPL互換の FLOSS ライセンスに基づく オープンソースソフトウエアとして配布が行われています。

引用元"SCILAB 日本語ページ"

環境構築

Scilabのインストール

"http://www.scilab.org"から、
インストーラ(2014/01/16 時点 Ver5.4.1)をダウンロード。

ダウンロードしたインストーラをインストール。

ScilabWaveletToolboxのインストール

Scilab起動後
「アプリケーション」→「モジュール管理」をクリック。

新たに表示されたウインドウから
「Signal Processing」→「ScilabWaveletToolbox」を選択し、
「インストール」ボタンをクリック。

インストール後Scilabを再起動すれば、環境構築完了。

データ処理

データの読込

振幅10, 2Hzの正弦波と、
振幅1 , 100Hzの正弦波を足し合わせた信号を作成。

Fs=1024;
t=soundsec(1,Fs);
wave=10*(sin(2*%pi*t*2))+(sin(2*%pi*t*100));
離散ウェーブレット変換の実行
[C,L]=wavedec(wave,2,"haar");

・戻り値
 C:ウェーブレット係数列
 L:分割レベル毎のウェーブレット係数のデータ数
・引数
 第1引数:離散ウェーブレット変換をしたい信号。
 第2引数:レベル(分解数)
 第3引数:マザーウェーブレット
  "haar": ハールウェーブレット
  "db1":ドベシ―ウェーブレット(N=1)
  "db2":ドベシ―ウェーブレット(N=2)

haar( "haar"), daubechies ("db1" to "db20"), coiflets ("coif1" to "coif5"), symlets ("sym2" to "sym20"), legendre ("leg1" to "leg9"), bathlets("bath4.0" to "bath4.15" and "bath6.0" to "bath6.15"), dmey ("dmey"), beyklin ("beylkin"), vaidyanathan ("vaidyanathan"), biorthogonal B-spline wavelets ("bior1.1" to "bior6.8"), "rbior1.1" to "rbior6.8"

引用元:help wavedec

結果出力

パラメータの取得

ウェーブレット変換を行うと、
信号はApproximation(低周波成分)と、Detail(高周波成分)に分かれるので、
ウェーブレット係数列Cからそれぞれのパラメータを取得する。

・Approximation
変換時に指定したレベルのApproximationを取得。

Ap2 = appcoef(C,L,"haar",2);

 第1引数:ウェーブレット係数列
 第2引数:分割レベル毎のウェーブレット係数のデータ数
 第3引数:マザーウェーブレット
 第4引数:レベル(分解数)
・Detail
分割数が2以上のときは、
レベル毎にDetailを取得。

De1 = detcoef(C,L,1);
De2 = detcoef(C,L,2);

 第1引数:ウェーブレット係数列
 第2引数:分割レベル毎のウェーブレット係数のデータ数
 第3引数:レベル(分解数)

パラメータのプロット

上から順に
 元信号
 Approximation(低周波成分 level2)
 Detail(高周波成分 level1)
 Detail(高周波成分 level2)
を並べてグラフ描画

subplot(411)
plot(wave)
plot(Ap2)
subplot(411)
plot(wave)
subplot(412)
plot(Ap2)
subplot(413)
plot(De1)
subplot(414)
plot(De2)

元信号が離散ウェーブレット変換により分解され、
高周波(200Hz)と低周波(2Hz)に信号が分かれているはず。
f:id:s_sikisya:20140117131947p:plain

参考
MATLAB-Wavelet Toolbox-
http://www.mathworks.com/tagteam/58032_TT031_Wavelet_Tlbx_Manual.pdf

オープンソースのウェーブレット変換ライブラリ

C言語系でウェーブレット変換を行うライブラリを調査。
GPLライセンスのライブラリなので、
組み込んだ際の配布時にはご注意を。

Geophysical Wavelet Library

http://users.math.uni-potsdam.de/~gwl/

blitzwave C++ Wavelet Library

http://oschulz.github.io/blitzwave/

Signal Processing Library in C++

https://code.google.com/p/tspl/

GNU Scientific Library

http://www.gnu.org/software/gsl/manual/html_node/Wavelet-Transforms.html

GPLに接触したくないなら、
1次元連続ウェーブレット変換限定になりますが、
以下を参考にするとよさげ。

C/C++言語でガボールウェーブレット変換により時間周波数解析を行うサンプルプログラム
http://hp.vector.co.jp/authors/VA046927/gabor_wavelet/gabor_wavelet.html
FFTを使った連続ウェーブレット変換の高速化
http://r9y9.github.io/blog/2013/10/20/continuous-wavelet-tranform/

C#でCOMポート番号とシリアル接続機器名を同時に取得する方法

マイコン機器とデータ通信を行う方法としては、
シリアル通信(RS-232)を行うことがメジャーだと思います。

.NET Framework 2.0以降ではSerialPortクラスが実装され、
シリアル通信を行うプログラムを書くのがお手軽になりました。
http://msdn.microsoft.com/ja-jp/library/system.io.ports.serialport(v=vs.110).aspx

有効なCOM番号の取得

シリアル機器とPCを接続しているときに、
SerialPortクラスのGetPortNameクラスを呼べば、
現在有効なCOM番号が出てきます。

ShowSerialPortNum.cs
using System;
using System.IO.Ports;

namespace ShowSerialPortNum
{
  class ShowSerialPortNum
  {
    public static void Main()
    {
    string[] ports = SerialPort.GetPortNames();
    foreach(string port in ports)
    {
      Console.WriteLine(port);
    }
    Console.ReadLine();
    }
  }
}

出力例(COM8が有効なとき)

COM8

接続している機器が1台だけなら気にせずそこに繋げればOK。
でも複数台接続をしていたらどのCOM番号が何と接続しているのかわからない。

そんなときは、
マイコンピュータ右クリック」→「プロパティ」
→「デバイスマネージャ」→「ポート(COMとLPT)」

とデバイスマネージャを参照して確認する必要があり、
ユーザーに対して不親切だし非常にメンドクサイ。

頼みのSerialPortクラスもそこまでは教えてくれないので、
頑張って別の方法を使って探しに行きます。

COM番号と接続機器名の取得

方法としてはWMIのWin32_PnPEntityクラスを使って、
上記したデバイスマネージャに記述されている内容を拾いに行きます。

ShowSerialPortName.cs
using System;
using System.Management;

namespace ShowSerialPortName
{
  class ShowSerialPortName
  {
    public static void Main()
    {
      string[] ports = GetDeviceNames();
      if(ports != null)
      {
        foreach (string port in ports)
        {
          Console.WriteLine(port);
        }
      }
      Console.ReadLine();
    }

    public static string[] GetDeviceNames()
    {
      var deviceNameList = new System.Collections.ArrayList();
      var check = new System.Text.RegularExpressions.Regex("(COM[1-9][0-9]?[0-9]?)");

      ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
      ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

      //全てのPnPデバイスを探索しシリアル通信が行われるデバイスを随時追加する
      foreach (ManagementObject manageObj in manageObjCol)
      {
        //Nameプロパティを取得
        var namePropertyValue = manageObj.GetPropertyValue("Name");
        if (namePropertyValue == null)
        {
          continue;
        }

        //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
        string name = namePropertyValue.ToString();
        if (check.IsMatch(name))
        {
          deviceNameList.Add(name);
        }
      }

      //戻り値作成
      if (deviceNameList.Count > 0)
      {
        string[] deviceNames = new string[deviceNameList.Count];
        int index = 0;
        foreach (var name in deviceNameList)
        {
          deviceNames[index++] = name.ToString();
        }
        return deviceNames; 
      }
      else
      {
        return null;
      }
    }
  }
}

出力例(COM8にArduino Unoを接続しているとき)

Arduino Uno (COM8)

やっていることとしては、
ManagementClassのコンストラクタ
Win32_PnPEntityクラスを指定して、
GetInstances()メソッドを呼び出すことで、
プラグアンドプレイデバイスのコレクション配列を取得

ManagementClass mcPnPEntity = new ManagementClass("Win32_PnPEntity");
ManagementObjectCollection manageObjCol = mcPnPEntity.GetInstances();

コレクションの要素を順次アクセスし、
GetPropertyValue関数でNameプロパティを取得。
Nameプロパティで得られたオブジェクトが、
デバイスマネージャ上で表示される値みたいです。
その後正規表現で"(COM1-999の番号)"が含まれるデータのみを抽出。

foreach (ManagementObject manageObj in manageObjCol)
{
  //Nameプロパティを取得
  var namePropertyValue = manageObj.GetPropertyValue("Name");
  if (namePropertyValue == null)
  {
    continue;
  }

  //Nameプロパティ文字列の一部が"(COM1)~(COM999)"と一致するときリストに追加"
  string name = namePropertyValue.ToString();
  if (check.IsMatch(name))
  {
    deviceNameList.Add(name);
  }
}

これでデバイス名とCOM番号を同時に取得します。
COM番号は文字列から数値を抜き出せばOKです。

参考
http://www-ice.yamagata-cit.ac.jp/ken../senshu/sitedev/index.php?AVR%2Favrdude36#xceb3d84
http://www.wmifun.net/library/win32_pnpentity.html

Androidで外部保存域の空き容量を取得する

Androidで無造作にデータを保存して、
空き容量が無いってことをしないために。

検索でよく引っかかったのが
StatFsクラスのgetAvailableBlocksメソッドを使う以下の例

long freeByteSize = 0;
String state = Environment.getExternalStorageState();

//外部保存領域が使用可能かチェック
if(Environment.MEDIA_MOUNTED.equals(state) || 
    Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
{
  //サイズ取得
  StatFs statFs = new StatFs(Environment.getExternalStorageDirectory().getAbsolutePath());
  freeByteSize = (long)statFs.getBlockSize() * (long)statFs.getAvailableBlocks();
}

しかしこのソースEclipse(ADT)では、

メソッド getBlockSize() は型 StatFs で使用すべきではありません
メソッド getAvailableBlocks() は型 StatFs で使用すべきではありません

と警告を受けます。

警告は無視しても作れますが気になるので別の方法を調べたところ、
どうやらAndroid API 9からは FileクラスのgetFreeSpaceが使えるらしい。

long freeByteSize = 0;
String state = Environment.getExternalStorageState();

//外部保存領域が使用可能かチェック
if(Environment.MEDIA_MOUNTED.equals(state) || 
    Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))
{
  //サイズ取得
  freeByteSize = Environment.getExternalStorageDirectory().getFreeSpace();
}

これで警告も消えて一安心。

参考
http://stackoverflow.com/questions/8133417/android-get-free-size-of-internal-external-memory
http://www.techdoctranslator.com/android/guide/data-storage