• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2016, 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.car.stream.telecom;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.ServiceConnection;
24 import android.os.AsyncTask;
25 import android.os.IBinder;
26 import android.os.SystemClock;
27 import android.telecom.Call;
28 import android.telecom.CallAudioState;
29 import android.telecom.TelecomManager;
30 import android.util.Log;
31 import com.android.car.stream.StreamCard;
32 import com.android.car.stream.StreamProducer;
33 import com.android.car.stream.telecom.StreamInCallService.StreamInCallServiceBinder;
34 
35 /**
36  * A {@link StreamProducer} that listens for active call events and produces a {@link StreamCard}
37  */
38 public class CurrentCallStreamProducer extends StreamProducer
39         implements StreamInCallService.InCallServiceCallback {
40     private static final String TAG = "CurrentCallProducer";
41 
42     private StreamInCallService mInCallService;
43     private PhoneCallback mPhoneCallback;
44     private CurrentCallActionReceiver mCallActionReceiver;
45     private Call mCurrentCall;
46     private long mCurrentCallStartTime;
47 
48     private CurrentCallConverter mConverter;
49     private AsyncTask mUpdateStreamItemTask;
50 
51     private String mDialerPackage;
52     private TelecomManager mTelecomManager;
53 
CurrentCallStreamProducer(Context context)54     public CurrentCallStreamProducer(Context context) {
55         super(context);
56     }
57 
58     @Override
start()59     public void start() {
60         super.start();
61         if (Log.isLoggable(TAG, Log.DEBUG)) {
62             Log.d(TAG, "current call producer started");
63         }
64         mTelecomManager = (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE);
65         mDialerPackage = mTelecomManager.getDefaultDialerPackage();
66         mConverter = new CurrentCallConverter(mContext);
67         mPhoneCallback = new PhoneCallback();
68 
69         Intent inCallServiceIntent = new Intent(mContext, StreamInCallService.class);
70         inCallServiceIntent.setAction(StreamInCallService.LOCAL_INCALL_SERVICE_BIND_ACTION);
71         mContext.bindService(inCallServiceIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
72     }
73 
74     @Override
stop()75     public void stop() {
76         mContext.unbindService(mServiceConnection);
77         super.stop();
78     }
79 
acceptCall()80     private void acceptCall() {
81         synchronized (mTelecomManager) {
82             if (mCurrentCall != null && mCurrentCall.getState() == Call.STATE_RINGING) {
83                 mCurrentCall.answer(0 /* videoState */);
84             }
85         }
86     }
87 
disconnectCall()88     private void disconnectCall() {
89         synchronized (mTelecomManager) {
90             if (mCurrentCall != null) {
91                 mCurrentCall.disconnect();
92             }
93         }
94     }
95 
96     @Override
onCallAdded(Call call)97     public void onCallAdded(Call call) {
98         if (Log.isLoggable(TAG, Log.DEBUG)) {
99             Log.d(TAG, "on call added, state: " + call.getState());
100         }
101         mCurrentCall = call;
102         updateStreamCard(mCurrentCall, mContext);
103         call.registerCallback(mPhoneCallback);
104     }
105 
106     @Override
onCallRemoved(Call call)107     public void onCallRemoved(Call call) {
108         if (Log.isLoggable(TAG, Log.DEBUG)) {
109             Log.d(TAG, "on call removed, state: " + call.getState());
110         }
111         call.unregisterCallback(mPhoneCallback);
112         updateStreamCard(call, mContext);
113         mCurrentCall = null;
114     }
115 
116     @Override
onCallAudioStateChanged(CallAudioState audioState)117     public void onCallAudioStateChanged(CallAudioState audioState) {
118         if (mCurrentCall != null && audioState != null) {
119             if (Log.isLoggable(TAG, Log.DEBUG)) {
120                 Log.d(TAG, "audio state changed, is muted? " + audioState.isMuted());
121             }
122             updateStreamCard(mCurrentCall, mContext);
123         }
124     }
125 
clearUpdateStreamItemTask()126     private void clearUpdateStreamItemTask() {
127         if (mUpdateStreamItemTask != null) {
128             mUpdateStreamItemTask.cancel(false);
129             mUpdateStreamItemTask = null;
130         }
131     }
132 
updateStreamCard(final Call call, final Context context)133     private void updateStreamCard(final Call call, final Context context) {
134         // Only one update may be active at a time.
135         clearUpdateStreamItemTask();
136 
137         mUpdateStreamItemTask = new AsyncTask<Void, Void, StreamCard>() {
138             @Override
139             protected StreamCard doInBackground(Void... voids) {
140                 try {
141                     return mConverter.convert(call, context, mInCallService.isMuted(),
142                             mCurrentCallStartTime, mDialerPackage);
143                 } catch (Exception e) {
144                     Log.e(TAG, "Failed to create StreamItem.", e);
145                     throw e;
146                 }
147             }
148 
149             @Override
150             protected void onPostExecute(StreamCard card) {
151                 if (call.getState() == Call.STATE_DISCONNECTED) {
152                     removeCard(card);
153                 } else {
154                     postCard(card);
155                 }
156             }
157         }.execute();
158     }
159 
160     private class PhoneCallback extends Call.Callback {
161         @Override
onStateChanged(Call call, int state)162         public void onStateChanged(Call call, int state) {
163             if (Log.isLoggable(TAG, Log.DEBUG)) {
164                 Log.d(TAG, "onStateChanged call: " + call + ", state: " + state);
165             }
166 
167             if (state == Call.STATE_ACTIVE) {
168                 mCurrentCallStartTime = SystemClock.elapsedRealtime();
169             } else {
170                 mCurrentCallStartTime = 0;
171             }
172 
173             switch (state) {
174                 // TODO: Determine if a HUD or stream card should be displayed.
175                 case Call.STATE_RINGING: // Incoming call is ringing.
176                 case Call.STATE_DIALING: // Outgoing call that is dialing.
177                 case Call.STATE_ACTIVE:  // Call is connected
178                 case Call.STATE_DISCONNECTING: // Call is being disconnected
179                 case Call.STATE_DISCONNECTED:  // Call has finished.
180                     updateStreamCard(call, mContext);
181                     mCurrentCall = call;
182                     break;
183                 default:
184             }
185         }
186     }
187 
188     private class CurrentCallActionReceiver extends BroadcastReceiver {
189         @Override
onReceive(Context context, Intent intent)190         public void onReceive(Context context, Intent intent) {
191             String intentAction = intent.getAction();
192             if (!TelecomConstants.INTENT_ACTION_STREAM_CALL_CONTROL.equals(intentAction)) {
193                 return;
194             }
195 
196             String action = intent.getStringExtra(TelecomConstants.EXTRA_STREAM_CALL_ACTION);
197             switch (action) {
198                 case TelecomConstants.ACTION_MUTE:
199                     mInCallService.setMuted(true);
200                     break;
201                 case TelecomConstants.ACTION_UNMUTE:
202                     mInCallService.setMuted(false);
203                     break;
204                 case TelecomConstants.ACTION_ACCEPT_CALL:
205                     acceptCall();
206                     break;
207                 case TelecomConstants.ACTION_HANG_UP_CALL:
208                     disconnectCall();
209                     break;
210                 default:
211             }
212         }
213     }
214 
215     private ServiceConnection mServiceConnection = new ServiceConnection() {
216         @Override
217         public void onServiceConnected(ComponentName name, IBinder service) {
218             StreamInCallServiceBinder binder = (StreamInCallServiceBinder) service;
219             mInCallService = binder.getService();
220             mInCallService.setCallback(CurrentCallStreamProducer.this);
221 
222             if (mCallActionReceiver == null) {
223                 mCallActionReceiver = new CurrentCallActionReceiver();
224                 mContext.registerReceiver(mCallActionReceiver,
225                         new IntentFilter(TelecomConstants.INTENT_ACTION_STREAM_CALL_CONTROL));
226             }
227         }
228 
229         @Override
230         public void onServiceDisconnected(ComponentName name) {
231             mInCallService = null;
232         }
233     };
234 }
235