結論からいうと私の環境では C:/Users/z4ck/AppData/Local/Android/Sdk/sources/android-29/以下にあった。
一般的に <sdkDir>/sources/android-<sdkVer>/ 以下にあると思われる。
一般的に <sdkDir>/sources/android-<sdkVer>/ 以下にあると思われる。
$ grep -R "/proc/" ../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java: BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/stat"));
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java: StringTokenizer stringTokenizer = new StringTokenizer(new BufferedReader(new FileReader(String.format("/proc/%d/stat", new Object[]{Integer.valueOf(iArr[i5])}))).readLine());
../../PerfMon-v1.21-java/sources/eu/chainfire/perfmon/ac.java: BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/net/dev"));
for (int i = 0; i < 256; i++) {
if (new File("/sys/devices/system/cpu/cpu" + String.valueOf(i)).exists() && i + 1 > this.a) {
this.a = i + 1;
}
}
![]() |
Android10でのPerfMon |
BufferedReader bufferedReader = new BufferedReader(new FileReader("/proc/stat"));
for (String readLine = bufferedReader.readLine(); readLine != null; readLine = bufferedReader.readLine()) {
if (readLine.startsWith("cpu")) {
次にこのコード。/proc/statの中身を読んでそれぞれの数値を抽出してる部分である。
ファイルの中身を読むbuffereReaderをsudo cat /proc/statの出力ストリームに変えればよさそうである。
while (i <= this.a) {
try {
agVarArr[i].j = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_min_freq", new Object[]{agVarArr[i].a}), 0);
if (agVarArr[i].j == 0) {
agVarArr[i].j = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_min_freq", new Object[]{agVarArr[i].a}), 0);
}
agVarArr[i].k = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_cur_freq", new Object[]{agVarArr[i].a}), 0);
if (agVarArr[i].k == 0) {
agVarArr[i].k = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_cur_freq", new Object[]{agVarArr[i].a}), 0);
}
agVarArr[i].l = a(String.format("/sys/devices/system/cpu/%s/cpufreq/scaling_max_freq", new Object[]{agVarArr[i].a}), 0);
if (agVarArr[i].l == 0) {
agVarArr[i].l = a(String.format("/sys/devices/system/cpu/%s/cpufreq/cpuinfo_max_freq", new Object[]{agVarArr[i].a}), 0);
}
agVarArr[0].j = Math.max(agVarArr[0].j, agVarArr[i].j);
agVarArr[0].k = Math.max(agVarArr[0].k, agVarArr[i].k);
agVarArr[0].l = Math.max(agVarArr[0].l, agVarArr[i].l);
i++;
} catch (Exception e3) {
}
}
/sys/devices/system/cpu/cpu[0-9]/cpufreq/は各コアの動作クロックの情報がある。a()という関数にかけてるのでa()を見てみる。
private int a(String str, int i) {
try {
return Integer.parseInt(new BufferedReader(new FileReader(str)).readLine(), 10);
} catch (Exception e2) {
return i;
}
}
/proc/statを読む部分と同じである。この部分もsuを使えば良さそうである。
実際にsuを使ったコードを書いてみる
public static BufferedReader sudoCat(String filename){
Process p;
BufferedReader reader;
try {
p = Runtime.getRuntime().exec("su");
DataOutputStream os = new DataOutputStream(p.getOutputStream());
os.writeBytes("cat "+filename+"\n");
os.writeBytes("exit\n");
os.flush();
p.waitFor();
reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
} catch (Exception e) {
e.printStackTrace();
reader = new BufferedReader(new StringReader("error"));
}
return reader;
}
このsudoCatメソッドをFileReaderの代わりに使えば良さそう。
さて、変更を加えたこのソースをビルドしたいのだがAndroidStudioのプロジェクトとして開くにはフォルダの構造が違いすぎる。apkのフォルダ構造をAndroidStudioのプロジェクトとして開くプラグインもコンバーターのようなツールも私の知る限りでは存在しない。
なのでapktoolを使う方が楽である。smaliで先ほど書いたsudoCatを書き、FileReaderが使われているところに置換する。ちなみにsmaliとはdalvikバイトコードのアセンブリである。
5行程度だったa()メソッドはsmaliでは以下のようになる。
.method private a(Ljava/lang/String;I)I
.locals 2
:try_start_0
new-instance v0, Ljava/io/BufferedReader;
new-instance v1, Ljava/io/FileReader;
invoke-direct {v1, p1}, Ljava/io/FileReader;->(Ljava/lang/String;)V
invoke-direct {v0, v1}, Ljava/io/BufferedReader;->(Ljava/io/Reader;)V
invoke-virtual {v0}, Ljava/io/BufferedReader;->readLine()Ljava/lang/String;
move-result-object v0
const/16 v1, 0xa
invoke-static {v0, v1}, Ljava/lang/Integer;->parseInt(Ljava/lang/String;I)I
:try_end_0
.catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0
move-result p2
:goto_0
return p2
:catch_0
move-exception v0
goto :goto_0
.end method
続く
![]() |
なぜかPerfMonが動作しているAndroid10 |
<activity android:excludeFromRecents="true" android:label="@string/app_name" android:launchMode="singleInstance" android:name=".LaunchActivity" android:theme="@android:style/Theme.NoDisplay">
LaunchActivityというクラスが最初に実行されるようだ。 public void onCreate(Bundle bundle) {
boolean z;
boolean z2 = false;
//~~長いので中略~~~
if (z || booleanExtra || !z2) {
a();
return;
}
public void a() {
boolean[] zArr = new boolean[4];
for (int i = 0; i < zArr.length; i++) {
zArr[i] = false;
}
if (PerfMonWindow.a(256)) {
StandOutWindow.b((Context) this, PerfMonWindow.class, 256);
zArr[0] = true;
}
if (PerfMonWindow.a(768)) {
StandOutWindow.b((Context) this, PerfMonWindow.class, 768);
zArr[1] = true;
}
if (PerfMonWindow.a(512)) {
StandOutWindow.b((Context) this, PerfMonWindow.class, 512);
zArr[2] = true;
}
if (PerfMonWindow.a(1024)) {
StandOutWindow.b((Context) this, PerfMonWindow.class, 1024);
zArr[3] = true;
}
Builder builder = VERSION.SDK_INT >= 11 ? new Builder(this, 2131099651) : new Builder(this);
builder.setTitle("PerfMon" + this.a);
builder.setItems(new CharSequence[]{"Foreground App", "CPU", "Disk I/O", "Network I/O", "Open all", "Close all", "My apps on Google Play", "Follow me on Twitter/G+"}, new d(this, zArr, this));
builder.setCancelable(true);
builder.setOnCancelListener(new e(this, zArr, this));
builder.show();
}
どうやらPerfMonWindowクラスのaメソッドを実行し、返り値次第でStandOutWindowのbメソッドも実行している。 public boolean a(int i, k kVar) {
y yVar = (y) a.get(Integer.valueOf(i));
if (yVar != null) {
yVar.c = false;
}
a.remove(Integer.valueOf(i));
return super.a(i, kVar);
}
public static HashMap a = new HashMap();
public void a(int i, FrameLayout frameLayout) {
if (i == 256) {
a.put(Integer.valueOf(i), new n(this, this, i, frameLayout));
}
if (i == 512) {
a.put(Integer.valueOf(i), new q(this, this, i, frameLayout));
}
if (i == 768) {
a.put(Integer.valueOf(i), new j(this, this, i, frameLayout));
}
if (i == 1024) {
a.put(Integer.valueOf(i), new u(this, this, i, frameLayout));
}
}
public n(PerfMonWindow perfMonWindow, Context context, int i, FrameLayout frameLayout) {
this.a = perfMonWindow;
super(perfMonWindow, context, i, frameLayout);
a(C0000R.layout.foreground_window, "Foreground App");
this.l = (TextView) this.g.findViewById(C0000R.id.stat_pkg);
this.m = (TextView) this.g.findViewById(C0000R.id.stat_pkg_name);
this.n = (TextView) this.g.findViewById(C0000R.id.stat_cpu);
this.o = (TextView) this.g.findViewById(C0000R.id.stat_cpu_detail);
this.p = (TextView) this.g.findViewById(C0000R.id.stat_memory);
this.q = (TextView) this.g.findViewById(C0000R.id.stat_memory_detail);
a(new View[]{this.l, this.m, this.n, this.o, this.p, this.q});
a();
this.c = true;
new Thread(new o(this)).start();
}
nクラス public q(PerfMonWindow perfMonWindow, Context context, int i, FrameLayout frameLayout) {
this.a = perfMonWindow;
super(perfMonWindow, context, i, frameLayout);
a(C0000R.layout.io_window, "Disk I/O");
this.l = (ListView) this.g.findViewById(C0000R.id.stat_list_ios);
this.c = true;
new Thread(new r(this)).start();
}
qクラス public j(PerfMonWindow perfMonWindow, Context context, int i, FrameLayout frameLayout) {
this.a = perfMonWindow;
super(perfMonWindow, context, i, frameLayout);
a(C0000R.layout.cpu_window, "CPU");
this.l = (ListView) this.g.findViewById(C0000R.id.stat_list_cpus);
this.c = true;
new Thread(new k(this)).start();
}
jクラス public u(PerfMonWindow perfMonWindow, Context context, int i, FrameLayout frameLayout) {
this.a = perfMonWindow;
super(perfMonWindow, context, i, frameLayout);
a(C0000R.layout.net_window, "Network I/O");
this.l = (ListView) this.g.findViewById(C0000R.id.stat_list_net);
this.c = true;
new Thread(new v(this)).start();
}
uクラスwei.mark.standout.StandOutWindow
調べたら2012年に作られたFloatingWindow用のライブラリということが分かった