1 /*
2  * Copyright (C) 2022 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 
18 package com.android.systemui.telephony.data.repository
19 
20 import android.annotation.SuppressLint
21 import android.content.Context
22 import android.content.pm.PackageManager
23 import android.telecom.TelecomManager
24 import android.telephony.Annotation
25 import android.telephony.TelephonyCallback
26 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
27 import com.android.systemui.dagger.SysUISingleton
28 import com.android.systemui.dagger.qualifiers.Application
29 import com.android.systemui.dagger.qualifiers.Background
30 import com.android.systemui.telephony.TelephonyListenerManager
31 import javax.inject.Inject
32 import kotlinx.coroutines.CoroutineDispatcher
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.channels.awaitClose
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.SharingStarted
37 import kotlinx.coroutines.flow.StateFlow
38 import kotlinx.coroutines.flow.flowOf
39 import kotlinx.coroutines.flow.map
40 import kotlinx.coroutines.flow.stateIn
41 import kotlinx.coroutines.withContext
42 
43 /** Defines interface for classes that encapsulate _some_ telephony-related state. */
44 interface TelephonyRepository {
45     /** The state of the current call. */
46     @Annotation.CallState val callState: Flow<Int>
47 
48     /**
49      * Whether there is an ongoing phone call (can be in dialing, ringing, active or holding states)
50      * originating from either a manager or self-managed {@link ConnectionService}.
51      */
52     val isInCall: StateFlow<Boolean>
53 
54     /** Whether the device has a radio that can be used for telephony. */
55     val hasTelephonyRadio: Boolean
56 }
57 
58 /**
59  * NOTE: This repository tracks only telephony-related state regarding the default mobile
60  * subscription. `TelephonyListenerManager` does not create new instances of `TelephonyManager` on a
61  * per-subscription basis and thus will always be tracking telephony information regarding
62  * `SubscriptionManager.getDefaultSubscriptionId`. See `TelephonyManager` and `SubscriptionManager`
63  * for more documentation.
64  */
65 @SysUISingleton
66 class TelephonyRepositoryImpl
67 @Inject
68 constructor(
69     @Application private val applicationScope: CoroutineScope,
70     @Application private val applicationContext: Context,
71     @Background private val backgroundDispatcher: CoroutineDispatcher,
72     private val manager: TelephonyListenerManager,
73     private val telecomManager: TelecomManager?,
74 ) : TelephonyRepository {
75 
76     @Annotation.CallState
<lambda>null77     override val callState: Flow<Int> = conflatedCallbackFlow {
78         val listener = TelephonyCallback.CallStateListener(::trySend)
79 
80         manager.addCallStateListener(listener)
81 
82         awaitClose { manager.removeCallStateListener(listener) }
83     }
84 
85     @SuppressLint("MissingPermission")
86     override val isInCall: StateFlow<Boolean> =
87         if (telecomManager == null) {
88                 flowOf(false)
89             } else {
<lambda>null90                 callState.map { withContext(backgroundDispatcher) { telecomManager.isInCall } }
91             }
92             .stateIn(
93                 applicationScope,
94                 SharingStarted.WhileSubscribed(),
95                 initialValue = false,
96             )
97 
98     override val hasTelephonyRadio =
99         applicationContext.packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)
100 }
101