1 /*
2  * Copyright (C) 2024 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 package com.android.systemui.car.ndo;
17 
18 import android.content.ComponentName;
19 import android.content.Context;
20 import android.media.session.MediaController;
21 import android.os.UserHandle;
22 import android.telecom.Call;
23 import android.util.Slog;
24 
25 import androidx.annotation.VisibleForTesting;
26 import androidx.lifecycle.LiveData;
27 import androidx.lifecycle.MediatorLiveData;
28 import androidx.lifecycle.ViewModel;
29 
30 import com.android.car.telephony.calling.InCallServiceManager;
31 import com.android.systemui.car.telecom.InCallServiceImpl;
32 
33 import java.beans.PropertyChangeEvent;
34 import java.beans.PropertyChangeListener;
35 import java.util.List;
36 
37 import javax.inject.Inject;
38 
39 /**
40  * ViewModel for blocking activities. Provides livedatas that listen to media and call state.
41  */
42 public class BlockerViewModel extends ViewModel implements PropertyChangeListener {
43     private static final String TAG = "SysUi.BlockerViewModel";
44     private static final String PROPERTY_IN_CALL_SERVICE = "PROPERTY_IN_CALL_SERVICE";
45     private final Context mContext;
46     private String mBlockedActivity;
47 
48     @VisibleForTesting
49     InCallLiveData mInCallLiveData;
50     private final InCallServiceManager mServiceManager;
51     @VisibleForTesting
52     MediaSessionHelper mMediaSessionHelper;
53     private final MediatorLiveData<BlockingType> mBlockingTypeLiveData = new MediatorLiveData<>();
54 
55     @Inject
BlockerViewModel(Context context, InCallServiceManager serviceManager)56     public BlockerViewModel(Context context, InCallServiceManager serviceManager) {
57         mContext = context;
58         mServiceManager = serviceManager;
59     }
60 
61     /** Initialize data sources **/
initialize(String blockedActivity, UserHandle userHandle)62     public void initialize(String blockedActivity, UserHandle userHandle) {
63         mBlockedActivity = blockedActivity;
64         mInCallLiveData = new InCallLiveData(mServiceManager, blockedActivity);
65 
66         // Listens to the call manager for when the inCallService is started after ABA.
67         mServiceManager.addObserver(this);
68         if (mServiceManager.getInCallService() != null) {
69             onInCallServiceConnected();
70         }
71 
72         mMediaSessionHelper = new MediaSessionHelper(mContext, userHandle);
73 
74         // Set initial liveData value
75         onUpdate();
76 
77         mBlockingTypeLiveData.addSource(mInCallLiveData, call -> onUpdate());
78         mBlockingTypeLiveData.addSource(mMediaSessionHelper.getActiveMediaSessions(),
79                 mediaSources -> onUpdate());
80     }
81 
82     /**
83      * Returns the livedata that indicates whether the blocked activity is voip or media.
84      */
getBlockingTypeLiveData()85     public LiveData<BlockingType> getBlockingTypeLiveData() {
86         return mBlockingTypeLiveData;
87     }
88 
89     /**
90      * {@link InCallServiceManager} will call this method to let it know the InCallService has been
91      * added.
92      */
93     @Override
propertyChange(PropertyChangeEvent propertyChangeEvent)94     public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
95         if (PROPERTY_IN_CALL_SERVICE.equals(propertyChangeEvent.getPropertyName())
96                 && mServiceManager.getInCallService() != null) {
97             onInCallServiceConnected();
98         }
99     }
100 
101     @Override
onCleared()102     public void onCleared() {
103         InCallServiceImpl inCallService = (InCallServiceImpl) mServiceManager.getInCallService();
104         if (inCallService != null) {
105             inCallService.removeListener(mInCallLiveData);
106         }
107         mServiceManager.removeObserver(this);
108         mMediaSessionHelper.cleanup();
109         mBlockingTypeLiveData.removeSource(mInCallLiveData);
110         mBlockingTypeLiveData.removeSource(mMediaSessionHelper.getActiveMediaSessions());
111     }
112 
113     @VisibleForTesting
onUpdate()114     void onUpdate() {
115         // Prioritize dialer first
116         Call call = mInCallLiveData.getValue();
117         if (call != null) {
118             mBlockingTypeLiveData.setValue(BlockingType.DIALER);
119         } else if (isBlockingActiveMediaSession(
120                 mMediaSessionHelper.getActiveMediaSessions().getValue())) {
121             mBlockingTypeLiveData.setValue(BlockingType.MEDIA);
122         } else {
123             mBlockingTypeLiveData.setValue(BlockingType.NONE);
124         }
125     }
126 
onInCallServiceConnected()127     private void onInCallServiceConnected() {
128         Slog.d(TAG, "inCallService Connected");
129         InCallServiceImpl inCallService = (InCallServiceImpl) mServiceManager.getInCallService();
130         inCallService.addListener(mInCallLiveData);
131     }
132 
133     /** @return whether the ABA is blocking an app with an active media session or not */
isBlockingActiveMediaSession(List<MediaController> mediaControllers)134     private boolean isBlockingActiveMediaSession(List<MediaController> mediaControllers) {
135         ComponentName componentName = ComponentName.unflattenFromString(mBlockedActivity);
136         if (componentName == null || mediaControllers == null) {
137             return false;
138         }
139 
140         for (MediaController mediaController : mediaControllers) {
141             if (mediaController.getPackageName().equals(componentName.getPackageName())) {
142                 return true;
143             }
144         }
145         return false;
146     }
147 
148     /**
149      * Enum for the different types of apps that are being blocked
150      */
151     public enum BlockingType {
152         NONE,
153         DIALER,
154         MEDIA
155     }
156 }
157