Android14の「写真と動画を選択」

Android14で端末中への画像アクセスが「OS側でユーザに画像を選ばせてアプリはその範囲しかアクセスできない」件、
他アプリはどうなのだろうと思って調べてみたらTwitter(X)はまだtargetSdkVersion33で未対応だった。

ThreadsはtargetSdkVersion34で一応対応してるが、古い作りの「アプリ内でギャラリーを表示する」から脱却していなかった。よって権限ダイアログで「写真と動画を選択」を選んだ場合(選択肢のトップなので、多くのユーザがこれを選んでしまうだろう)、その後にOSが提供するダイアログで「アプリがどの画像にアクセスできるか」を選び、さらにアプリ内ギャラリーでもう一度画像を選択する形になる。2回選択するというのが非常にわかりにくい。

その後画像をさらに追加したい場合どうするかというと、投稿作成画面のサムネイルをタップすると再度アプリ内ギャラリーが開くが、この時点ではOSから許可されていないので追加の画像はギャラリーには存在しない。アプリ内ギャラリーに「一部の写真と動画に対して、Threadsによるアクセスを許可しました。」という注意書きがうっすら見えて、その右の管理するボタンを押すと「さらに写真を選択」「設定を変更」の選択肢がある。
「さらに写真を選択」はOSによる画像選択画面からやり直してOSがアプリにアクセス許可する画像を増やして、その後にアプリ内ギャラリーに候補が追加されて、ユーザは再度画像を選ぶ。2回選択するというのが非常にわかりにくい。

「設定を変更」はアプリの権限設定への導線で、「写真と動画へのアクセス」を「常に全て許可」にさせたいのだろう。これを行うとアプリ内ギャラリーには端末中の画像が全て表示され、Android 13までと同様の操作感になる。

権限を一度取得したらアプリから端末上の画像を全部読める、という時代は終わるのだと思う。それはそうだ、公開したい写真を選ぶためだけに、公開したくない写真にまでアプリがアクセスできるのは危険だろう。悪意のあるアプリがウラでどこかにアップロードしていてもユーザは気が付かない。

過去のアプリのUXは、端末上の全てにアクセスできる前提で、端末上の画像を選ぶ画面をアプリ側で作り込んでいた。その画面に、投稿中の画像の順序を変更する機能も持たせていた。

おそらく今後は端末上の画像を選ぶ画面はセキュリティのためにOSに任せるべきで、投稿中の画像の順序を変更する機能は別の場所に設ける必要がある。

あと、OSで選択した画像のUriは権限の種別によりpickなんとかという内容になってしまい、過去のMediaStoreのUriとは異なる。過去のMediaStoreのUriでクエリしても空カーソルが返る。例えば過去のアプリではSAFのDocument Uri をMediaStoreのUriに変換したりしていたが、そういう芸当はもう出来なくなる。

rsync のmanual の"Include/Exclude Pattern Rules" の日本語訳

原文: "Include/Exclude Pattern Rules" section in https://linux.die.net/man/1/rsync

+-などのフィルター・ルールを使ってパターンを指定することで、ファイルをインクルードしたり、除外したりすることができます(上記の「フィルター・ルール」のセクションで紹介されています)。

include/excludeルールはそれぞれ、転送されるファイル名とマッチするパターンを指定します。

これらのパターンにはいくつかの形式があります:

  • パターンが/で始まる場合は、ファイル階層の特定の場所に固定され、そうでない場合はパス名の末尾にマッチする。 これは正規表現における先頭の^に似ている。したがって、/fooは、トランスファーのroot(グローバルルールの場合)、またはマージファイルのディレクト(ディレクトリごとのルールの場合) のいずれかにあるfooの名前にマッチします。非限定fooは、ツリー内のどこでもfooの名前にマッチする。これは、アルゴリズムトップダウン再帰的に適用されるためである。(訳注:パターン先頭の/で)アンカーされていないsub/fooであっても、foosubという名前のディレクトリの中で見つかった場合、階層内のどのポイントでもマッチする。 転送のルートでマッチするパターンを指定する方法の詳細については、ANCHORING INCLUDE/EXCLUDE PATTERNSのセクションを参照のこと。
  • パターンが/で終わっている場合は、ディレクトリにのみマッチし、通常のファイル、シンボリックリンク、デバイスにはマッチしない。
  • rsyncは単純な文字列マッチングを行うかワイルドカードマッチングを行うかを、パターンに *?[ の3つのワイルドカード文字のいずれかが含まれているかどうかチェックして選択します。
  • 単一の* はany path componentにマッチするが、スラッシュにマッチしない。
  • ** を使うとスラッシュを含むanythingにマッチする。
  • ? はスラッシュ以外のany character (1文字) にマッチする。
  • [ は文字クラス指定を開始する。[a-z][[:alpha:]]のような。
  • ワイルドカード・パターンでは、バックスラッシュはワイルドカード文字をエスケープするために使うことができるが、ワイルドカードが存在しない場合は文字通りにマッチする。
  • パターンが / (末尾の / はカウントしない) または ** を含む場合、先頭のディレクトリを含む完全なパス名に対してマッチされる。パターンに/**が含まれていない場合は、ファイル名の最後のコンポーネントに対してのみマッチする。(このアルゴリズム再帰的に適用されるので、full filenameは実際には開始ディレクトリから下のパスのどの部分にもなりうることを思い出してください)。
  • 末尾にdir_name/***を指定すると、ディレクトリ(dir_name/が指定されたかのように)とディレクトリ内のすべて(dir_name/**が指定されたかのように)の両方にマッチします。この動作はバージョン2.6.7で追加された。

element-matrix.juggler.jpの見た目を軽く変えてみる

Matrixの Element-web を動かしている element-matrix.juggler.jpの見た目を軽く変えてみる。 別におしゃれにしたい訳ではなく、Google Safe Browsing の フィッシングサイト誤検知にひっかかってしまったから。 とりあえず素のElement-webと区別できるようにしたい。

config.json

以下のような内容を追加/変更する。

{

    (snip…)

    "defaultCountryCode": "JP",
    "brand": "juggler.jp Matrix サービス",
    "branding" :{
        "welcome_background_url": "https://matrix-element.juggler.jp/x/bg.jpg",
        "auth_header_logo_url": "https://matrix-element.juggler.jp/x/logo.png",
        "auth_footer_links" : [
            {
                "text":"Juggler.jp Matrix サービスについて",
                "url":"https://tateisu.hatenablog.com/entry/2023/01/20/164203"
            }
        ]
    },
    "embedded_pages": {
        "welcome_url": "https://matrix-element.juggler.jp/x/welcome.html"
    }
}

項目の説明は element-web高s機器にある。 github.com

welcome_url

embedded_pages.welcome_url にはHTML断片が入ったURLを指定できる。 今回は超シンプルに以下のようなもの。

<br/>
<div  style="background-color:#fff;padding: 1em;">
<h1>Juggler.jp Matrix サービス<br/>"Element" Web UI</h1>

<H3>matrix.juggler.jp を Element Web UI で使う</h3>
<ul>
<li><a href="https://matrix-element.juggler.jp/#/register">ユーザ登録</a></li>
<li><a href="https://matrix-element.juggler.jp/#/login">ログイン</a></li>
</ul>

<H3>このサイトの説明</h3>
<ul>
<li><a href="https://tateisu.hatenablog.com/entry/2023/01/20/164203">Juggler.jp Matrix サービスについて</a></li>
<li><a href="https://matrix.org/">Powered by Matrix</a></li>
</ul>
</div>

PostgreSQL 15 で pg_basebackup したデータの復旧テスト

pg_basebackupしたデータからの復旧テスト

バックアップしたデータ

base.tar.gz
backup_manifest
pg_wal.tar.gz
package_versions
  • package_versions は dpkg -l |grep postgres を記録したもの。
  • 残り3つは pg_basebackup が出力したファイル。

package_versionsの内容

rc  pgdg-keyring                          2018.2                                  all          keyring for apt.postgresql.org
ii  postgresql-15                         15.1-1.pgdg22.04+1                      amd64        The World's Most Advanced Open Source Relational Database
ii  postgresql-client                     15+246.pgdg22.04+1                      all          front-end programs for PostgreSQL (supported version)
ii  postgresql-client-15                  15.1-1.pgdg22.04+1                      amd64        front-end programs for PostgreSQL 15
ii  postgresql-client-common              246.pgdg22.04+1                         all          manager for multiple PostgreSQL client versions
ii  postgresql-common                     246.pgdg22.04+1                         all          PostgreSQL database-cluster manager
ii  postgresql-contrib                    15+246.pgdg22.04+1                      all          additional facilities for PostgreSQL (supported version)
ii  postgresql-doc                        15+246.pgdg22.04+1                      all          documentation for the PostgreSQL database management system
ii  postgresql-doc-15                     15.1-1.pgdg22.04+1                      all          documentation for the PostgreSQL database management system

dockerコンテナで復旧テスト

コンテナの起動

$ docker run -v /x/backup/postgres/20230121-050015:/backup:ro --rm -it ubuntu:22.04 bash

OSバージョンの確認

# cat /etc/os-release
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

必要そうなパッケージのインストール

apt update
apt install whiptail locales lv nano sudo curl ca-certificates gnupg lsb-release

ロケールの作成

  • ubuntuコンテナだと en_us.UTF-8 がない。
  • DBデータ復旧後の起動でログに問題が報告されるので、用意する。
apt update && apt install whiptail locales
dpkg-reconfigure locales

バックアップデータの確認とWALアーカイブの展開

  • dockerコンテナ起動時にバインドマウントしたフォルダ
  • WALアーカイブは適当にフォルダを作って展開しておく
  • パーミッションをDBが読めるものにする
# export BACKUP_DIR=/backup
# find $BACKUP_DIR -ls
# mkdir /walArchive
# cd /walArchive
# tar xzf $BACKUP_DIR/pg_wal.tar.gz
# chmod -R a+rw .
# find . -ls

Postgres リポジトリの導入

# curl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor > /etc/apt/trusted.gpg.d/apt.postgresql.org.gpg
# echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list
# cat /etc/apt/sources.list.d/pgdg.list

バージョンを指定してインストール

apt update
apt-get install postgresql-15=15.1-1.pgdg22.04+1 postgresql-client-15=15.1-1.pgdg22.04+1

インストール中にタイムゾーンを尋ねられる。今回は Asia/Tokyo を指定した。

DB起動/接続のテスト

# service --status-all
# service postgresql start
# service --status-all
(postgresサービスが起動していること)
# ps auxwf
(プロセス一覧にpostgresが見えること)
# lv /var/log/postgresql/postgresql-15-main.log
2023-01-21 10:49:52.237 JST [6265] LOG:  starting PostgreSQL 15.1 (Ubuntu 15.1-1.pgdg22.04+1) on x86_64-pc-linux-gnu,
 compiled by gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0, 64-bit
2023-01-21 10:49:52.237 JST [6265] LOG:  listening on IPv4 address "127.0.0.1", port 5432
2023-01-21 10:49:52.237 JST [6265] LOG:  could not bind IPv6 address "::1": Cannot assign requested address
2023-01-21 10:49:52.242 JST [6265] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
2023-01-21 10:49:52.249 JST [6268] LOG:  database system was shut down at 2023-01-21 10:49:23 JST
2023-01-21 10:49:52.254 JST [6265] LOG:  database system is ready to accept connections

接続テスト

# sudo -u postgres psql -c "\\l"
                                                 List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    | ICU Locale | Locale Provider |   Access privileges
-----------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
           |          |          |             |             |            |                 | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
           |          |          |             |             |            |                 | postgres=CTc/postgres
(3 rows)

DBを止める

# service postgresql stop
# ps auxwf
(postgresのプロセスが存在しないこと)
# lv /var/log/postgresql/postgresql-15-main.log
2023-01-21 10:53:47.786 JST [6265] LOG:  received fast shutdown request
2023-01-21 10:53:47.796 JST [6265] LOG:  aborting any active transactions
2023-01-21 10:53:47.798 JST [6265] LOG:  background worker "logical replication launcher" (PID 6271) exited with exit
 code 1
2023-01-21 10:53:47.798 JST [6266] LOG:  shutting down
2023-01-21 10:53:47.801 JST [6266] LOG:  checkpoint starting: shutdown immediate
2023-01-21 10:53:47.830 JST [6266] LOG:  checkpoint complete: wrote 43 buffers (0.3%); 0 WAL file(s) added, 0 removed
, 0 recycled; write=0.006 s, sync=0.012 s, total=0.033 s; sync files=11, longest=0.003 s, average=0.002 s; distance=2
52 kB, estimate=252 kB
2023-01-21 10:53:47.834 JST [6265] LOG:  database system is shut down

データ破損

  • DBは止めておく
cd /var/lib/postgresql/15/main/
rm -fr ./*

データの復旧

cd /var/lib/postgresql/15/main/
tar xvfz $BACKUP_DIR/base.tar.gz

restore_command の確認と設定

# grep -rin restore /etc/postgresql/15/main/ 
./postgresql.conf:268:#restore_command = ''             # command to use to restore an archived logfile segment
./postgresql.conf:269:                          # placeholders: %p = path of file to restore
./postgresql.conf:282:#recovery_target_name = ''        # the named restore point to which recovery will proceed

設定されてないので設定する。

nano /etc/postgresql/15/main/postgresql.conf

CTRL-W を押して restore_command を入力してEnter。 既存の行は変えず、次の行を挿入。

restore_command = 'cp /walArchive/%f %p'
archive_cleanup_command = 'pg_archivecleanup /walArchive %r'

CTRL-Oを押してファイル名をかえずにEnterして保存。 CTRL-Xで終了。

起動

service postgresql start

ログの確認

lv /var/log/postgresql/postgresql-15-main.log
(snip)
2023-01-21 10:57:04.170 JST [6401] LOG:  restored log file "00000001000000770000003B" from archive
2023-01-21 10:57:04.211 JST [6401] LOG:  redo starts at 77/3B000028
(snip)

データを読めているか

# sudo -u postgres psql -c "\\l"
                                                  List of databases
   Name    |   Owner   | Encoding |   Collate   |    Ctype    | ICU Locale | Locale Provider |   Access privileges
-----------+-----------+----------+-------------+-------------+------------+-----------------+-----------------------
 lemmy     | lemmy     | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 mastodon2 | mastodon2 | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 mastodon3 | mastodon3 | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 matrix1   | matrix1   | UTF8     | C           | C           |            | libc            |
 misskey11 | misskey11 | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 misskey12 | misskey12 | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 postgres  | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            |
 template0 | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
           |           |          |             |             |            |                 | postgres=CTc/postgres
 template1 | postgres  | UTF8     | en_US.UTF-8 | en_US.UTF-8 |            | libc            | =c/postgres          +
           |           |          |             |             |            |                 | postgres=CTc/postgres
(9 rows)

この記事に含まれないもの

  • /etc/postgresql/15/main/ の復旧。 多分 /etc まるごとバックアップしてると思うので…

matrix.juggler.jp

Juggler.jp Matrix サービスについて

MatrixはOSSのメッセージサービスです。

  • 分散型、非中央集権型
  • 強力な暗号化機能があります。

Juggler.jp Matrix サービスは以下の2つを提供しています。

メモ:

  • バックエンドサーバ(matrix,juggler.jp) と WebUI (matrix-element.juggler.jp) は完全に分離しています。
  • 他のMatrixサーバにあるルームにも参加できます。
  • アプリも色々あります。Elementアプリは Web以外にも、PCデスクトップ版、Android版、iOS版を利用できます。

導入手順

matrix.juggler.jp の告知やメンテナンスは?

2023/02/17 フィッシングサイトと偽検出されてしまった件

Google Safe Browsingさんにフィッシングサイトと偽検出されてしまい、各種ブラウザでもページを開くと警告が出るようになってしまいました。
また、警告をスキップした後でもFirefoxだとプラウザの設定を変えないと Element-web と matrix.juggler.jp の間の通信ができません。

Firefoxのセーフブラウジング設定

Googleへサイトの再審査要求を出して改善を待っている状態です。

mastodon.juggler.jp

2023/01/19 DBロストのお知らせ

matrix.juggler.jp というサービスのDBをロストさせてしまいました…。
DB完全初期化なので、ユーザは新規登録からやりなおしです。
今後はDBバックアップに不備がないよう努めてまいります。

Huawei端末の「デフォルトの保存場所」と externalCacheDir と FileProvider.parsePathStrategy

Huawei端末はデフォルトの保存先をSDカードにすることができる。

私の手元では以下の2機種にその設定があった。

f:id:tateisu:20211121141542p:plain

f:id:tateisu:20211121141524p:plain

その設定により、context.externalCacheDir は SDカードのパスを返す。
しかし ContextCompat.getExternalCacheDirs(context)[0] はプライマリストレージのパスを返す。

FileProvider#parsePathStrategy が参照するのは後者であり、よってFileProvider経由で外部アプリに公開したいならアプリでも ContextCompat.getExternalCacheDirs(context)[0] を使ってファイル保存を行うべきだ。

結果としてキャッシュデータはプライマリストレージに保存されてユーザの意向は反映されなくなるが、動作しないよりはマシ。

Android Emoji Policy と DefaultEmojiCompatConfig と Playサービス依存の罠

PlayストアでAndroid Emoji Policy なるものが要求されるようになりました。
Developer Program Policy: October 27, 2021 announcement - Play Console Help
「Apps running on Android 12 and above must comply with the latest Unicode version within 4 months of public availability.」
だそうで、Android 12 以上の端末で動作するアプリは最新の絵文字に4ヶ月以内に対応しなければならないそうです。

それに伴い AndroidX AppCompat 1.4.0 と AndroidX Emoji2 1.0.0 がリリースされました。

https://developer.android.com/jetpack/androidx/releases/emoji2 の下の方を見ると以下のようなことが書いてあります。

  • appcompat は androidx.emoji2 を依存関係に持つよ。
  • androidx.startup を使って EmojiCompatInitializer による自動設定を粉うよ。
  • DefaultEmojiCompatConfig を使ってデフォルトのフォントプロバイダーを探すよ。
  • 自動初期化を無効にするにはうんぬんかんぬん。

しかし試しにアプリを書いてみると古い端末では13.1の絵文字は表示されません。

コードを読んで原因をみてみましょう。

  • androidx.emoji2.text.DefaultEmojiCompatConfigは queryIntentContentProviders(Intent(action="androidx.content.action.LOAD_EMOJI_FONT")) で端末中のContentProviderを探す。
  • (providerInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == ApplicationInfo.FLAG_SYSTEM を満たさないものを除外する。
  • com.google.android.gms.fonts だけが残る。
  • そのContentProviderと連携して何かする。

バリバリにPlayサービスに依存してますね。当然それなりに新しいPlayサービスが入ってないと自動ロードは働きません。
手元の端末だとXperia 10 III はオッケー、Xperia Z3 Compact はダメでした。

絵文字プロバイダがない場合は EmojiCompat.Config 相当のオブジェクトが生成されず、EmojiCompat.init はまだ呼び出されていない状態になります。
このままだとEmojiTextViewはインスタンス生成時に例外を出します。AppCompatTextViewは端末OSに入ってる絵文字を表示します。
ユーザ体験的には残念ですね。

古い端末向けのワークアラウンド

  • 依存関係にimplementation "androidx.emoji2:emoji2-bundled:$emojiVersion"を追加。
  • アプリ初期化や画面初期化の際に EmojiCompat.init(BundledEmojiCompatConfig(applicationContext)) を呼び出す。

すると「まだEmojiCompatが初期化されていなければ」アプリにバンドルした絵文字データが使われるようになります。