Androidにプリビルドjarライブラリを追加してフレームワーク内で呼び出す


## 行うこと

1. テストjarライブラリを作成してAOSPにjarライブラリを追加
2. フレームワーク内(クイック設定パネル)から1で追加したライブラリのAPIの呼び出し
3. AWSのライブラリを追加してフレームワーク内から呼び出してみる

## 1. テストjarライブラリ作成

テスト文字列"test"を返す簡易的なAPIを実装したjarライブラリを作成する。

```java:Test.java
package com.testlib;


public class Test {
    public String getTestString() {
        return "test";
    }
}

```

- jar作成

```bash
$ javac Test.java
$ jar -cvf test-lib.jar *.class
```

上記で作成されたjarをAOSPソースの以下にコピー

/prebuilts/misc/common/test-lib/test-lib.jar

Android.mkも作成

```makefile:/prebuilts/misc/common/test-lib/Android.mk
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional
LOCAL_PREBUILT_JAVA_LIBRARIES := test-lib$(COMMON_JAVA_PACKAGE_SUFFIX)

include $(BUILD_HOST_PREBUILT)
```

## 2. クイック設定パネルから1で追加したライブラリのAPIの呼び出し

### 2-1. ソース修正

適当に追加したクイック設定パネルのタイルをタップ時に、上記APIから取得した文字列"test"をToastで表示するようにAOSPソースに修正を入れる。

[diff](https://github.com/pondelion/frameworks_base/commit/df9f43f8caed078996ce184b4131ff12d6dc9f54)

```diff:/frameworks/base/packages/SystemUI/src/com/android/systemui/qs/tiles/LoggingTile.java
+import android.widget.Toast;
...
+import com.testlib.Test;
...
    @Override
    protected void handleClick() {
+        Test test = new Test();
+        Toast.makeText(mContext , test.getTestString(), Toast.LENGTH_SHORT).show();
        try {
```

```diff:/frameworks/base/packages/SystemUI/Android.mk
...
LOCAL_STATIC_JAVA_LIBRARIES := \
    SystemUI-tags \
    SystemUI-proto

LOCAL_STATIC_JAVA_LIBRARIES += test-lib
...
include $(BUILD_PACKAGE)

+include $(CLEAR_VARS)

+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := test-lib:../../../../prebuilts/misc/common/test-lib/test-+lib.jar#libs/test_lib/test-lib.jar

+include $(BUILD_MULTI_PREBUILT)

include $(call all-makefiles-under,$(LOCAL_PATH))
...

```

### 2-2. ビルド

- ビルド

```bash
$ make -j4
```

- SystemUI差し替え

```bash
$ adb remount
$ adb push out/target/product/bullhead/system/priv-app/SystemUI /system/priv-app/
$ adb reboot
```

### 2-3. 動作確認

タイルをタップしてみる

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38382/3e8a633b-0f45-212e-b360-d25204c6f858.png" width=30%>

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38382/a4aee3e9-8baa-e547-dd37-47d015b43344.png" width=30%>

追加したライブラリのAPIが正常に呼ばれていることが確認できる。

## 3. AWSのライブラリを追加してフレームワーク内から呼び出してみる

### 3-1. ライブラリ準備

AWS SDK for Androidの

- aws-android-sdk-core.jar
- aws-android-sdk-lambda.jar
- aws-android-sdk-s3.jar
- aws-android-sdk-ddb.jar
- aws-android-sdk-ddb-mapper.jar
- aws-android-sdk-cognito.jar

を/prebuilts/misc/common/aws配下にコピーする。

Gsonが見つからないとエラーが出たのでGsonのjarも/prebuilts/misc/common/gsonにコピーしておく。

### 3-2. Lambdaを使ってみる

同様にタイルタップ時にLambdaを呼び出すように改変する。

公式参考
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/with-android-example.html
https://docs.aws.amazon.com/ja_jp/aws-mobile/latest/developerguide/how-to-android-lambda.html

[ソース修正差分](https://github.com/pondelion/frameworks_base/commit/e8f4b429319c44e4c7dc92e19e8f00497852d1b4)

### 3-3. S3にbugreportを送ってみる

設定からもbugreportは取得できるが、上記同様にタイルタップ時にbugreportを取得されるように導線をいれる。

```diff:packages/SystemUI/src/com/android/systemui/qs/tiles/LoggingTile.java
+    private Handler mHandler = new Handler();
...
    @Override
    protected void handleClick() {
+        mHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    ActivityManager.getService().requestBugReport(
+                        ActivityManager.BUGREPORT_OPTION_INTERACTIVE
+                    );
+                } catch (RemoteException e) {
+                    Log.e(TAG, "requestBugReport() failed");
+                }
+            }
+        }, 500);
        try {
```

また、bugreport取得完了時に下記がコールされるようなので、下記にS3にbugreportを保存する処理を追加する。

frameworks/base/packages/Shell/src/com/android/shell/BugreportProgressService.java#onBugreportFinished

```diff:frameworks/base/packages/Shell/src/com/android/shell/BugreportProgressService.java
    private void onBugreportFinished(int id, Intent intent) {
        final File bugreportFile = getFileExtra(intent, EXTRA_BUGREPORT);
        if (bugreportFile == null) {
            // Should never happen, dumpstate always set the file.
            Log.wtf(TAG, "Missing " + EXTRA_BUGREPORT + " on intent " + intent);
            return;
        }
+
+        try {
+            final String S3_ACCESS_KEY = "YOUR_ACCESS_KEY";
+            final String S3_SECRET_KEY = "YOUR_SECRET_KEY";
+            AmazonS3Client S3Client = new AmazonS3Client(
+                new BasicAWSCredentials(S3_ACCESS_KEY, S3_SECRET_KEY)
+            );
+            final String BUCKET_NAME = "aosp_test";
+            PutObjectRequest por = new PutObjectRequest(
+                BUCKET_NAME,
+                bugreportFile.getName(),
+                bugreportFile
+            );
+            S3Client.putObject(por);
+        } catch (final Exception e) {
+            Log.e(TAG, "Failed to save bugreport to S3", e);
+        }

        mInfoDialog.onBugreportFinished();
        BugreportInfo info = getInfo(id);
```

[ソース修正差分](https://github.com/pondelion/frameworks_base/commit/0e58b7170c9630563f0484693c6c198ed26d0a9d)

ビルドと差し替え

```bash
$ make -j4
$ adb remount
$ adb push out/target/product/bullhead/system/priv-app/SystemUI /system/priv-app/
$ adb push out/target/product/bullhead/system/priv-app/Shell /system/priv-app/
$ adb reboot
```

タイルをタップしてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38382/be253475-ce2e-2e61-f5ba-abe5975923e0.png)

S3に保存されていることが確認できる。

### 3-4. DynamoDBにレコードを保存してみる

[ソース修正差分](https://github.com/pondelion/frameworks_base/commit/7df3b16b9b61553f9963a824626a35bd1936daad)

タイルタップ後、DynamoDBにレコードが保存されていることが確認できる↓

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/38382/32f012f1-ec67-3ab6-9e67-fb2f1ac95fc5.png)



---

## メモ

### エラー１

起動後permission android.permission.READ_CONTACTSが～といったFatal ExceptionでSystemUIが落ちることがあったが、単にFDRしたら解決。

```
01-22 18:59:06.852: D/AndroidRuntime(4784): Shutting down VM
01-22 18:59:06.855: E/AndroidRuntime(4784): FATAL EXCEPTION: main
01-22 18:59:06.855: E/AndroidRuntime(4784): Process: com.android.systemui, PID: 4784
01-22 18:59:06.855: E/AndroidRuntime(4784): java.lang.RuntimeException: Unable to create service com.android.systemui.SystemUIService: android.view.InflateException: Binary XML file line #73: uid=10062 needs permission android.permission.READ_CONTACTS to read lock_screen_owner_info_enabled for user 0
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.handleCreateService(ActivityThread.java:3349)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.-wrap4(Unknown Source:0)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Handler.dispatchMessage(Handler.java:106)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Looper.loop(Looper.java:164)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.main(ActivityThread.java:6494)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at java.lang.reflect.Method.invoke(Native Method)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
01-22 18:59:06.855: E/AndroidRuntime(4784): Caused by: android.view.InflateException: Binary XML file line #73: uid=10062 needs permission android.permission.READ_CONTACTS to read lock_screen_owner_info_enabled for user 0
01-22 18:59:06.855: E/AndroidRuntime(4784): Caused by: java.lang.SecurityException: uid=10062 needs permission android.permission.READ_CONTACTS to read lock_screen_owner_info_enabled for user 0
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Parcel.readException(Parcel.java:2004)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Parcel.readException(Parcel.java:1950)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.widget.ILockSettings$Stub$Proxy.getBoolean(ILockSettings.java:476)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.widget.LockPatternUtils.getBoolean(LockPatternUtils.java:1271)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.widget.LockPatternUtils.isOwnerInfoEnabled(LockPatternUtils.java:738)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.keyguard.KeyguardStatusView.getOwnerInfo(KeyguardStatusView.java:273)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.keyguard.KeyguardStatusView.updateOwnerInfo(KeyguardStatusView.java:244)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.keyguard.KeyguardStatusView.onFinishInflate(KeyguardStatusView.java:170)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflate(LayoutInflater.java:876)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.parseInclude(LayoutInflater.java:995)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflate(LayoutInflater.java:859)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.parseInclude(LayoutInflater.java:995)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflate(LayoutInflater.java:859)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:824)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:515)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:423)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.LayoutInflater.inflate(LayoutInflater.java:374)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.view.View.inflate(View.java:23239)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.statusbar.phone.StatusBar.inflateStatusBarWindow(StatusBar.java:1440)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.statusbar.phone.StatusBar.makeStatusBarView(StatusBar.java:1009)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.statusbar.phone.StatusBar.addStatusBarWindow(StatusBar.java:3664)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.statusbar.phone.StatusBar.createAndAddWindows(StatusBar.java:3660)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.statusbar.phone.StatusBar.start(StatusBar.java:889)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.SystemBars.createStatusBarFromConfig(SystemBars.java:71)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.SystemBars.start(SystemBars.java:42)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:215)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.SystemUIApplication.startServicesIfNeeded(SystemUIApplication.java:164)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.systemui.SystemUIService.onCreate(SystemUIService.java:33)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.handleCreateService(ActivityThread.java:3339)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.-wrap4(Unknown Source:0)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1677)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Handler.dispatchMessage(Handler.java:106)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.os.Looper.loop(Looper.java:164)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at android.app.ActivityThread.main(ActivityThread.java:6494)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at java.lang.reflect.Method.invoke(Native Method)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
01-22 18:59:06.855: E/AndroidRuntime(4784): 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
```

### エラー2

DynamoDBで、DynamoDBHashKeyは付けているにもかかわらず、下記のように見つからない旨のエラーが出た。

```
11-06 00:22:42.371: E/AndroidRuntime(832): FATAL EXCEPTION: Thread-3
11-06 00:22:42.371: E/AndroidRuntime(832): Process: com.android.systemui, PID: 832
11-06 00:22:42.371: E/AndroidRuntime(832): com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMappingException: No interface com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBHashKey annotation found in class class com.android.systemui.qs.tiles.AOSPTest
11-06 00:22:42.371: E/AndroidRuntime(832): 	at com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper.needAutoGenerateAssignableKey(DynamoDBMapper.java:944)
11-06 00:22:42.371: E/AndroidRuntime(832): 	at com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper.save(DynamoDBMapper.java:1008)
11-06 00:22:42.371: E/AndroidRuntime(832): 	at com.amazonaws.mobileconnectors.dynamodbv2.dynamodbmapper.DynamoDBMapper.save(DynamoDBMapper.java:909)
11-06 00:22:42.371: E/AndroidRuntime(832): 	at com.android.systemui.qs.tiles.LoggingTile$1.run(LoggingTile.java:57)
```

proguardのファイルに下記のように例外登録するのがミソな模様

```
-keep class com.android.systemui.qs.tiles.AOSPTest { *; }
```