Slide 1

Slide 1 text

Android FTP 立 Yoshihiro Wada / @e10dokup 2025/2/12 @ CA.aab #5

Slide 2

Slide 2 text

{ “id”: “@e10dokup”, “name”: “Yoshihiro Wada”, “affiliations”: [ “CyberAgent Inc, / Ameba” ], “interested”: [ “camera”, “gadget”, “driving”, “motorsports” ] }

Slide 3

Slide 3 text

Android FTP 車 立 一 見 行 手 Foreground Service Android Developers FTP 日 3

Slide 4

Slide 4 text

Android FTP 立 見 Yoshihiro Wada / @e10dokup 2025/2/12 @ CA.aab #5

Slide 5

Slide 5 text

大 FTP

Slide 6

Slide 6 text

FTP File Transfer Protocol HTTP FTP FTP FTP RFC 959 FTP 6

Slide 7

Slide 7 text

FTP FTP Cyberduck - FTP ftp:// URL 立 FTP 7

Slide 8

Slide 8 text

FTP Android

Slide 9

Slide 9 text

Q: Android FTP FTP FTP Play 9

Slide 10

Slide 10 text

10 Sony Transfer & Tagging Add-on 用

Slide 11

Slide 11 text

Android FTP 立 Q: Android FTP A: 見 11

Slide 12

Slide 12 text

FTP 方 方 FTP 方 12

Slide 13

Slide 13 text

FTP 方 方 FTP 方 13

Slide 14

Slide 14 text

Sony Transfer & Tagging add-on OSS 示 The Apache FtpServer Android FTP ppareit/swiftp 見 1 FTP FTP 方 14

Slide 15

Slide 15 text

大 15 Ҿ༻:https://github.com/ppareit/swiftp/tree/master/app/src/main/java/be/ppareit/swiftp/server

Slide 16

Slide 16 text

Sony Transfer & Tagging add-on OSS 示 The Apache FtpServer Android FTP ppareit/swiftp 見 1 FTP FTP 方 16

Slide 17

Slide 17 text

Javadoc 17 Ҿ༻:https://mina.apache.org/ftpserver-project/index.html

Slide 18

Slide 18 text

FTP 方 方 FTP 方 18

Slide 19

Slide 19 text

Android 自身 IP DNS Android API 自 Android Developers Network FTP 19

Slide 20

Slide 20 text

20 Ҿ༻:https://developer.android.com/develop/connectivity/network-ops/reading-network-state ConnectivityManager

Slide 21

Slide 21 text

FTP 方 方 FTP 方 21

Slide 22

Slide 22 text

FTP 長 Service 22 Ҿ༻:https://developer.android.com/develop/background-work/services

Slide 23

Slide 23 text

23 Ҿ༻:https://developer.android.com/develop/background-work/services#Types-of-services

Slide 24

Slide 24 text

行 行 用 Foreground Service 24 Ҿ༻:https://developer.android.com/develop/background-work/services/fgs

Slide 25

Slide 25 text

No content

Slide 26

Slide 26 text

Jetpack Compose IP ConnectivityManager IP 用 UI 26 private fun getCurrentIpAddress(): String { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val currentNetwork = connectivityManager.activeNetwork val linkProperties = connectivityManager.getLinkProperties(currentNetwork) return linkProperties?.linkAddresses?.first { it.address is Inet4Address }.toString() }

Slide 27

Slide 27 text

曰 LinkProperties IP Android API 行 linkProperties 見 27 Ҿ༻: https://developer.android.com/develop/connectivity/network-ops/reading-network-state https://developer.android.com/reference/android/net/LinkProperties#getLinkAddresses()

Slide 28

Slide 28 text

行 Foreground Service 用 Service 用 Foreground Foreground Service 行 28

Slide 29

Slide 29 text

行 Foreground Service 用 Service 用 Foreground Foreground Service 行 29

Slide 30

Slide 30 text

Activity 用 override Service 用 30 class FtpService : Service() { override fun onBind(intent: Intent?): IBinder? { return null } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { // ࣮ࡍʹαʔϏεʹ͍ͤͨ͞ॲཧΛ͜͜ʹهड़͢Δ return START_STICKY } override fun onDestroy() { super.onDestroy() } }

Slide 31

Slide 31 text

行 Foreground Service 用 Service 用 Foreground Foreground Service 行 31

Slide 32

Slide 32 text

Service Activity Manifest 見 Foreground Service 言 32 Ҿ༻:https://developer.android.com/develop/background-work/services/fgs/declare#declare-fgs

Slide 33

Slide 33 text

foregroundServiceType Android 14 API 34 Foreground Service 33 Ҿ༻:https://developer.android.com/develop/background-work/services/fgs/service-types

Slide 34

Slide 34 text

foregroundServiceType Android 14 API 34 色 Data sync Foreground Service 34 Ҿ༻:https://developer.android.com/develop/background-work/services/fgs/service-types#data-sync

Slide 35

Slide 35 text

見 Android Manifest Manifest Foreground Service 言 35 …

Slide 36

Slide 36 text

行 Foreground Service 用 Service 用 Foreground Foreground Service 行 36

Slide 37

Slide 37 text

Service onStartCommand 行 用 ServiceCompat.startForeground 行 用 Foreground Service 37 Ҿ༻:https://developer.android.com/develop/background-work/services/fgs/launch#promote-service

Slide 38

Slide 38 text

Foreground Service 用 Foreground Service 38 try { ServiceCompat.startForeground( this, ID_FOREGROUND_SERVICE_FTP, createNotification(), // ௨஌Λ࡞Δؔ਺ʢ࣍ϖʔδʣ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC } else { 0 } // ManifestͰએݴͨ͠Foreground Service TypeΛ౰ͯࠐΉ ) // TODO: Foregroundʹग़དྷͨΒ͜͜ͰFTPαʔόΛ࣮ߦ͍ͨ͠ } catch (e: Exception) { Timber.e(e, "Failed to launch foreground service”) }

Slide 39

Slide 39 text

用 Foreground Service 39 private fun createNotification(): Notification { val channel = NotificationChannel( NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT ) val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) // ௨஌νϟωϧͷઃఆ val notification = NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID).apply { setContentTitle("FTP Server") setContentText("FTP Server established") setSmallIcon(R.drawable.notification_icon) }.build() // ࣮ࡍͷ௨஌ͷੜ੒ return notification }

Slide 40

Slide 40 text

Activity ForegroundService 行 行 Foreground Service 40 private fun launchFtpService() { val intent = Intent(this, FtpService::class.java) startForegroundService(intent) }

Slide 41

Slide 41 text

Apache FTP Server 見 立 方 FTP 方 Foreground Service FTP 行 41

Slide 42

Slide 42 text

方 FTP 立 方 42 Ҿ༻:https://mina.apache.org/ftpserver-project/embedding_ftpserver.html

Slide 43

Slide 43 text

PropertiesUserManager 人 FTPS 方 43 Ҿ༻:https://mina.apache.org/ftpserver-project/embedding_ftpserver.html

Slide 44

Slide 44 text

Listener UserManager 行 FTP 44 private fun startFtpServer() { // Apache FTP ServerͷϑΝΫτϦ val serverFactory = FtpServerFactory() // ϙʔτͳͲΛࢦఆ͢ΔListenerΛ࡞Δ val ftpListener = createFtpListener() // Ϣʔβ໊ɺύεϫʔυΛ؅ཧ͢ΔUserManagerΛ࡞Δ val userManager = createUserManager() // ࡞ͬͨListenerɺUserManagerΛࢦఆ͠ɺFTPαʔόΛ࣮ࡍʹ࡞੒͠ɺ࣮ߦ͢Δ serverFactory.addListener("default", ftpListener) serverFactory.userManager = userManager ftpServer = serverFactory.createServer() ftpServer?.start() }

Slide 45

Slide 45 text

Listener UserManager 行 FTP 45 private fun startFtpServer() { // Apache FTP ServerͷϑΝΫτϦ val serverFactory = FtpServerFactory() // ϙʔτͳͲΛࢦఆ͢ΔListenerΛ࡞Δ val ftpListener = createFtpListener() // Ϣʔβ໊ɺύεϫʔυΛ؅ཧ͢ΔUserManagerΛ࡞Δ val userManager = createUserManager() // ࡞ͬͨListenerɺUserManagerΛࢦఆ͠ɺFTPαʔόΛ࣮ࡍʹ࡞੒͠ɺ࣮ߦ͢Δ serverFactory.addListener("default", ftpListener) serverFactory.userManager = userManager ftpServer = serverFactory.createServer() ftpServer?.start() }

Slide 46

Slide 46 text

FTP Listener 自 Listener 46 private fun createFtpListener(): Listener { val listenerFactory = ListenerFactory() // ࣮ࡍʹ͸Ҿ਺Λड͚औͬͯࢦఆ͢Δͱྑͦ͞͏ listenerFactory.port = <ϙʔτ൪߸> return listenerFactory.createListener() }

Slide 47

Slide 47 text

Listener UserManager 行 FTP 47 private fun startFtpServer() { // Apache FTP ServerͷϑΝΫτϦ val serverFactory = FtpServerFactory() // ϙʔτͳͲΛࢦఆ͢ΔListenerΛ࡞Δ val ftpListener = createFtpListener() // Ϣʔβ໊ɺύεϫʔυΛ؅ཧ͢ΔUserManagerΛ࡞Δ val userManager = createUserManager() // ࡞ͬͨListenerɺUserManagerΛࢦఆ͠ɺFTPαʔόΛ࣮ࡍʹ࡞੒͠ɺ࣮ߦ͢Δ serverFactory.addListener("default", ftpListener) serverFactory.userManager = userManager ftpServer = serverFactory.createServer() ftpServer?.start() }

Slide 48

Slide 48 text

UserManager 48 private fun createUserManager(): UserManager { val userManagerFactory = PropertiesUserManagerFactory() // Ϣʔβ৘ใΛอ؅͢ΔϑΝΠϧͷ࡞੒ val profileFile = File(getExternalFilesDir(null)?.path + "/users.property") if (!profileFile.exists()) { profileFile.createNewFile() } userManagerFactory.file = profileFile userManagerFactory.passwordEncryptor = SaltedPasswordEncryptor() // Ϣʔβ໊ɺύεϫʔυɺݖݶͳͲͷઃఆ val baseUser = BaseUser() baseUser.name = <Ϣʔβ໊> baseUser.password = <ύεϫʔυ> baseUser.homeDirectory = getExternalFilesDir(Environment.DIRECTORY_DCIM)?.path baseUser.authorities = listOf(WritePermission()) val userManager = userManagerFactory.createUserManager() userManager.save(baseUser) return userManager }

Slide 49

Slide 49 text

Listener UserManager 行 FTP 49 private fun startFtpServer() { // Apache FTP ServerͷϑΝΫτϦ val serverFactory = FtpServerFactory() // ϙʔτͳͲΛࢦఆ͢ΔListenerΛ࡞Δ val ftpListener = createFtpListener() // Ϣʔβ໊ɺύεϫʔυΛ؅ཧ͢ΔUserManagerΛ࡞Δ val userManager = createUserManager() // ࡞ͬͨListenerɺUserManagerΛࢦఆ͠ɺFTPαʔόΛ࣮ࡍʹ࡞੒͠ɺ࣮ߦ͢Δ serverFactory.addListener("default", ftpListener) serverFactory.userManager = userManager ftpServer = serverFactory.createServer() ftpServer?.start() }

Slide 50

Slide 50 text

Foreground Service 行 行 50 try { ServiceCompat.startForeground( this, ID_FOREGROUND_SERVICE_FTP, createNotification(), // ௨஌Λ࡞Δؔ਺ʢ࣍ϖʔδʣ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC } else { 0 } // ManifestͰએݴͨ͠Foreground Service TypeΛ౰ͯࠐΉ ) // FTPαʔόͷ࣮ߦ startFtpServer() } catch (e: Exception) { Timber.e(e, "Failed to launch foreground service”) } Cyberduck Android FTP 子

Slide 51

Slide 51 text

Android FTP 立 子 走 FTP 自 大 Android Developers 大 GitHub OSS 51