• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2025 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.privatespace.filetransfer
18 
19 import android.app.Service
20 import android.content.Intent
21 import android.net.Uri
22 import android.os.Environment
23 import android.os.IBinder
24 import android.util.Log
25 import java.util.ArrayList
26 import kotlinx.coroutines.CoroutineScope
27 import kotlinx.coroutines.Dispatchers
28 import kotlinx.coroutines.launch
29 
30 /**
31  * An object responsible for starting a foreground service to copy/move the user selected files into
32  * private space.
33  */
34 class FileTransferService(
35     private val serviceScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
36 ) : Service() {
37     companion object {
38         /**
39          * A boolean extra noting whether this transfer is a copy(true) or a move(false) operation.
40          */
41         const val KEEP_ORIGINAL_EXTRA: String = "keep_original_extra"
42         /** An array extra to get the uris of the file to transfer. */
43         const val SOURCE_URIS_EXTRA: String = "source_uris_extra"
44         /**
45          * Target relative path where the given file(s) should be saved. This path should be inline
46          * with guidelines for {@link MediaStore.MediaColumns.RELATIVE_PATH}
47          */
48         const val DESTINATION_PATH_EXTRA: String = "destination_path"
49 
50         private const val TAG: String = "FileTransferService"
51     }
52 
onStartCommandnull53     override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
54         val uris: ArrayList<Uri>? =
55             intent.getParcelableArrayListExtra(SOURCE_URIS_EXTRA, Uri::class.java)
56         val keepOriginal = intent.getBooleanExtra(KEEP_ORIGINAL_EXTRA, true)
57         val destinationPath =
58             intent.getStringExtra(DESTINATION_PATH_EXTRA) ?: Environment.DIRECTORY_DOWNLOADS
59 
60         uris?.let {
61             val numberOfFiles = it.size
62             if (numberOfFiles == 0) {
63                 Log.e(TAG, "No files to transfer")
64                 return@let
65             }
66 
67             val notificationsHelper = NotificationsHelper(applicationContext)
68             notificationsHelper.createNotificationChannel()
69 
70             val fileTransferImpl = FileTransferManagerImpl(notificationsHelper, applicationContext)
71 
72             serviceScope.launch {
73                 startForeground(
74                     NotificationsHelper.NOTIFICATION_ID,
75                     notificationsHelper.buildProgressNotification(0, numberOfFiles, keepOriginal),
76                 )
77                 fileTransferImpl.transferFiles(it, keepOriginal, destinationPath)
78                 stopForeground(STOP_FOREGROUND_DETACH)
79                 stopSelf()
80             }
81         }
82             ?:
83             // TODO(b/401000421): Notify the user that we are unable to read the files
84             Log.e(TAG, "Unable to parse uris from the intent: $intent")
85 
86         return START_NOT_STICKY
87     }
88 
onBindnull89     override fun onBind(intent: Intent?): IBinder? {
90         return null
91     }
92 }
93