• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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.googlecode.android_scripting.facade.telephony;
18 
19 import java.io.UnsupportedEncodingException;
20 import java.lang.reflect.Field;
21 import java.net.URLEncoder;
22 import java.util.Iterator;
23 import java.util.List;
24 
25 import android.app.Service;
26 import android.content.ContentResolver;
27 import android.content.Intent;
28 import android.database.Cursor;
29 import android.telecom.PhoneAccount;
30 import android.telecom.PhoneAccountHandle;
31 import android.telecom.TelecomManager;
32 import android.telecom.VideoProfile;
33 import android.telephony.SubscriptionManager;
34 import android.telephony.TelephonyManager;
35 import android.net.Uri;
36 import android.provider.ContactsContract;
37 
38 import com.googlecode.android_scripting.Log;
39 import com.googlecode.android_scripting.facade.AndroidFacade;
40 import com.googlecode.android_scripting.facade.EventFacade;
41 import com.googlecode.android_scripting.facade.FacadeManager;
42 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
43 import com.googlecode.android_scripting.rpc.Rpc;
44 import com.googlecode.android_scripting.rpc.RpcDefault;
45 import com.googlecode.android_scripting.rpc.RpcOptional;
46 import com.googlecode.android_scripting.rpc.RpcParameter;
47 
48 /**
49  * Exposes TelecomManager functionality.
50  */
51 public class TelecomManagerFacade extends RpcReceiver {
52 
53     private final Service mService;
54     private final AndroidFacade mAndroidFacade;
55 
56     private final TelecomManager mTelecomManager;
57     private final TelephonyManager mTelephonyManager;
58 
59     private List<PhoneAccountHandle> mEnabledAccountHandles = null;
60 
TelecomManagerFacade(FacadeManager manager)61     public TelecomManagerFacade(FacadeManager manager) {
62         super(manager);
63         mService = manager.getService();
64         mTelecomManager = new TelecomManager(mService);
65         mTelephonyManager = new TelephonyManager(mService);
66         mAndroidFacade = manager.getReceiver(AndroidFacade.class);
67         InCallServiceImpl.setEventFacade(
68                 manager.getReceiver(EventFacade.class));
69     }
70 
71     @Override
shutdown()72     public void shutdown() {
73         InCallServiceImpl.setEventFacade(null);
74     }
75 
76     @Rpc(description = "If there's a ringing call, accept on behalf of the user.")
telecomAcceptRingingCall( @pcParametername = "videoState") @pcOptional String videoState)77     public void telecomAcceptRingingCall(
78             @RpcParameter(name = "videoState")
79             @RpcOptional
80             String videoState) {
81 
82         if (videoState == null) {
83             mTelecomManager.acceptRingingCall();
84         }
85         else {
86             int state = InCallServiceImpl.getVideoCallState(videoState);
87 
88             if (state == InCallServiceImpl.STATE_INVALID) {
89                 Log.e("telecomAcceptRingingCall: video state is invalid!");
90                 return;
91             }
92 
93             mTelecomManager.acceptRingingCall(state);
94         }
95     }
96 
97     @Rpc(description = "Removes the missed-call notification if one is present.")
telecomCancelMissedCallsNotification()98     public void telecomCancelMissedCallsNotification() {
99         mTelecomManager.cancelMissedCallsNotification();
100     }
101 
102     @Rpc(description = "Remove all Accounts that belong to the calling package from the system.")
telecomClearAccounts()103     public void telecomClearAccounts() {
104         mTelecomManager.clearAccounts();
105     }
106 
107     @Rpc(description = "End an ongoing call.")
telecomEndCall()108     public Boolean telecomEndCall() {
109         return mTelecomManager.endCall();
110     }
111 
112     @Rpc(description = "Get a list of all PhoneAccounts.")
telecomGetAllPhoneAccounts()113     public List<PhoneAccount> telecomGetAllPhoneAccounts() {
114         return mTelecomManager.getAllPhoneAccounts();
115     }
116 
117     @Rpc(description = "Get the current call state.")
telecomGetCallState()118     public String telecomGetCallState() {
119         int state = mTelecomManager.getCallState();
120         return TelephonyUtils.getTelephonyCallStateString(state);
121     }
122 
123     @Rpc(description = "Get the current tty mode.")
telecomGetCurrentTtyMode()124     public String telecomGetCurrentTtyMode() {
125         int mode = mTelecomManager.getCurrentTtyMode();
126         return TelephonyUtils.getTtyModeString(mode);
127     }
128 
129     @Rpc(description = "Bring incallUI to foreground.")
telecomShowInCallScreen( @pcParametername = "showDialpad") @pcOptional @pcDefault"false") Boolean showDialpad)130     public void telecomShowInCallScreen(
131             @RpcParameter(name = "showDialpad")
132             @RpcOptional
133             @RpcDefault("false")
134             Boolean showDialpad) {
135         mTelecomManager.showInCallScreen(showDialpad);
136     }
137 
138     @Rpc(description = "Get the list of PhoneAccountHandles with calling capability.")
telecomGetEnabledPhoneAccounts()139     public List<PhoneAccountHandle> telecomGetEnabledPhoneAccounts() {
140         mEnabledAccountHandles = mTelecomManager.getCallCapablePhoneAccounts();
141         return mEnabledAccountHandles;
142     }
143 
144     @Rpc(description = "Set the user-chosen default PhoneAccount for making outgoing phone calls.")
telecomSetUserSelectedOutgoingPhoneAccount( @pcParametername = "phoneAccountHandleId") String phoneAccountHandleId)145     public void telecomSetUserSelectedOutgoingPhoneAccount(
146                         @RpcParameter(name = "phoneAccountHandleId")
147             String phoneAccountHandleId) throws Exception {
148 
149         List<PhoneAccountHandle> accountHandles = mTelecomManager
150                 .getAllPhoneAccountHandles();
151         for (PhoneAccountHandle handle : accountHandles) {
152             if (handle.getId().equals(phoneAccountHandleId)) {
153                 mTelecomManager.setUserSelectedOutgoingPhoneAccount(handle);
154                 Log.d(String.format("Set default Outgoing Phone Account(%s)",
155                         phoneAccountHandleId));
156                 return;
157             }
158         }
159         Log.d(String.format(
160                 "Failed to find a matching phoneAccountHandleId(%s).",
161                 phoneAccountHandleId));
162         throw new Exception(String.format(
163                 "Failed to find a matching phoneAccountHandleId(%s).",
164                 phoneAccountHandleId));
165     }
166 
167     @Rpc(description = "Get the user-chosen default PhoneAccount for making outgoing phone calls.")
telecomGetUserSelectedOutgoingPhoneAccount()168     public PhoneAccountHandle telecomGetUserSelectedOutgoingPhoneAccount() {
169         return mTelecomManager.getUserSelectedOutgoingPhoneAccount();
170     }
171 
172     @Rpc(description = "Set the PhoneAccount corresponding to user selected subscription id " +
173                        " for making outgoing phone calls.")
telecomSetUserSelectedOutgoingPhoneAccountBySubId( @pcParametername = "subId") Integer subId)174     public void telecomSetUserSelectedOutgoingPhoneAccountBySubId(
175                         @RpcParameter(name = "subId")
176                         Integer subId) throws Exception {
177           Iterator<PhoneAccountHandle> phoneAccounts =
178                mTelecomManager.getCallCapablePhoneAccounts().listIterator();
179 
180           while (phoneAccounts.hasNext()) {
181               PhoneAccountHandle phoneAccountHandle = phoneAccounts.next();
182               PhoneAccount phoneAccount =
183                        mTelecomManager.getPhoneAccount(phoneAccountHandle);
184               if (subId == mTelephonyManager.getSubIdForPhoneAccount(phoneAccount)) {
185                   mTelecomManager.setUserSelectedOutgoingPhoneAccount(phoneAccountHandle);
186                   Log.d(String.format(
187                       "Set default Outgoing Phone Account for subscription(%s)", subId));
188                   return;
189               }
190           }
191           Log.d(String.format(
192                   "Failed to find a matching Phone Account for subscription (%s).",
193                   subId));
194           throw new Exception(String.format(
195                   "Failed to find a matching Phone Account for subscription (%s).",
196                    subId));
197     }
198 
199     @Rpc(description = "Returns whether there is an ongoing phone call.")
telecomIsInCall()200     public Boolean telecomIsInCall() {
201         return mTelecomManager.isInCall();
202     }
203 
204     @Rpc(description = "Returns whether there is a ringing incoming call.")
telecomIsRinging()205     public Boolean telecomIsRinging() {
206         return mTelecomManager.isRinging();
207     }
208 
209     @Rpc(description = "Silences the rigner if there's a ringing call.")
telecomSilenceRinger()210     public void telecomSilenceRinger() {
211         mTelecomManager.silenceRinger();
212     }
213 
214     @Rpc(description = "Swap two calls")
telecomSwapCalls()215     public void telecomSwapCalls() {
216         // TODO: b/26273475 Add logic to swap the foreground and back ground calls
217     }
218 
219     @Rpc(description = "Start listening for added calls")
telecomStartListeningForCallAdded()220     public void telecomStartListeningForCallAdded() {
221         InCallServiceImpl.CallListener.startListeningForEvent(
222                 InCallServiceImpl.CallListener.LISTEN_CALL_ADDED);
223     }
224 
225     @Rpc(description = "Stop listening for added calls")
telecomStopListeningForCallAdded()226     public void telecomStopListeningForCallAdded() {
227         InCallServiceImpl.CallListener.stopListeningForEvent(
228                 InCallServiceImpl.CallListener.LISTEN_CALL_ADDED);
229     }
230 
231     @Rpc(description = "Start listening for removed calls")
telecomStartListeningForCallRemoved()232     public void telecomStartListeningForCallRemoved() {
233         InCallServiceImpl.CallListener.startListeningForEvent(
234                 InCallServiceImpl.CallListener.LISTEN_CALL_REMOVED);
235     }
236 
237     @Rpc(description = "Stop listening for removed calls")
telecomStopListeningForCallRemoved()238     public void telecomStopListeningForCallRemoved() {
239         InCallServiceImpl.CallListener.stopListeningForEvent(
240                 InCallServiceImpl.CallListener.LISTEN_CALL_REMOVED);
241     }
242 
243     @Rpc(description = "Toggles call waiting feature on or off for default voice subscription id.")
toggleCallWaiting( @pcParametername = "enabled") @pcOptional Boolean enabled)244     public void toggleCallWaiting(
245             @RpcParameter(name = "enabled")
246             @RpcOptional
247             Boolean enabled) {
248         toggleCallWaitingForSubscription(
249                 SubscriptionManager.getDefaultVoiceSubscriptionId(), enabled);
250     }
251 
252     @Rpc(description = "Toggles call waiting feature on or off for specified subscription id.")
toggleCallWaitingForSubscription( @pcParametername = "subId") @pcOptional Integer subId, @RpcParameter(name = "enabled") @RpcOptional Boolean enabled)253     public void toggleCallWaitingForSubscription(
254             @RpcParameter(name = "subId")
255             @RpcOptional
256             Integer subId,
257             @RpcParameter(name = "enabled")
258             @RpcOptional
259             Boolean enabled) {
260         // TODO: b/26273478 Enable or Disable the call waiting feature
261     }
262 
263     @Rpc(description = "Sends an MMI string to Telecom for processing")
telecomHandleMmi( @pcParametername = "dialString") String dialString)264     public void telecomHandleMmi(
265                         @RpcParameter(name = "dialString")
266             String dialString) {
267         mTelecomManager.handleMmi(dialString);
268     }
269 
270     // TODO: b/20917712 add support to pass arbitrary "Extras" object
271     // for videoCall parameter
272     @Deprecated
273     @Rpc(description = "Calls a phone by resolving a generic URI.")
telecomCall( @pcParametername = "uriString") final String uriString, @RpcParameter(name = "videoCall") @RpcOptional @RpcDefault("false") Boolean videoCall)274     public void telecomCall(
275                         @RpcParameter(name = "uriString")
276             final String uriString,
277             @RpcParameter(name = "videoCall")
278             @RpcOptional
279             @RpcDefault("false")
280             Boolean videoCall) throws Exception {
281 
282         Log.w("Function telecomCall is deprecated; please use a URI-specific call");
283 
284         Uri uri = Uri.parse(uriString);
285         if (uri.getScheme().equals("content")) {
286             telecomCallContentUri(uriString, videoCall);
287         }
288         else {
289             telecomCallNumber(uriString, videoCall);
290         }
291     }
292 
293     // TODO: b/20917712 add support to pass arbitrary "Extras" object
294     // for videoCall parameter
295     @Rpc(description = "Calls a phone by resolving a Content-type URI.")
telecomCallContentUri( @pcParametername = "uriString") final String uriString, @RpcParameter(name = "videoCall") @RpcOptional @RpcDefault("false") Boolean videoCall)296     public void telecomCallContentUri(
297                         @RpcParameter(name = "uriString")
298             final String uriString,
299             @RpcParameter(name = "videoCall")
300             @RpcOptional
301             @RpcDefault("false")
302             Boolean videoCall)
303             throws Exception {
304         Uri uri = Uri.parse(uriString);
305         if (!uri.getScheme().equals("content")) {
306             Log.e("Invalid URI!!");
307             return;
308         }
309 
310         String phoneNumberColumn = ContactsContract.PhoneLookup.NUMBER;
311         String selectWhere = null;
312         if ((FacadeManager.class.cast(mManager)).getSdkLevel() >= 5) {
313             Class<?> contactsContract_Data_class =
314                     Class.forName("android.provider.ContactsContract$Data");
315             Field RAW_CONTACT_ID_field =
316                     contactsContract_Data_class.getField("RAW_CONTACT_ID");
317             selectWhere = RAW_CONTACT_ID_field.get(null).toString() + "="
318                     + uri.getLastPathSegment();
319             Field CONTENT_URI_field =
320                     contactsContract_Data_class.getField("CONTENT_URI");
321             uri = Uri.parse(CONTENT_URI_field.get(null).toString());
322             Class<?> ContactsContract_CommonDataKinds_Phone_class =
323                     Class.forName("android.provider.ContactsContract$CommonDataKinds$Phone");
324             Field NUMBER_field =
325                     ContactsContract_CommonDataKinds_Phone_class.getField("NUMBER");
326             phoneNumberColumn = NUMBER_field.get(null).toString();
327         }
328         ContentResolver resolver = mService.getContentResolver();
329         Cursor c = resolver.query(uri, new String[] {
330                 phoneNumberColumn
331         },
332                 selectWhere, null, null);
333         String number = "";
334         if (c.moveToFirst()) {
335             number = c.getString(c.getColumnIndexOrThrow(phoneNumberColumn));
336         }
337         c.close();
338         telecomCallNumber(number, videoCall);
339     }
340 
341     // TODO: b/20917712 add support to pass arbitrary "Extras" object
342     // for videoCall parameter
343     @Rpc(description = "Calls a phone number.")
telecomCallNumber( @pcParametername = "number") final String number, @RpcParameter(name = "videoCall") @RpcOptional @RpcDefault("false") Boolean videoCall)344     public void telecomCallNumber(
345                         @RpcParameter(name = "number")
346             final String number,
347             @RpcParameter(name = "videoCall")
348             @RpcOptional
349             @RpcDefault("false")
350             Boolean videoCall)
351             throws Exception {
352         telecomCallTelUri("tel:" + URLEncoder.encode(number, "ASCII"), videoCall);
353     }
354 
355     // TODO: b/20917712 add support to pass arbitrary "Extras" object
356     // for videoCall parameter
357     @Rpc(description = "Calls a phone by Tel-URI.")
telecomCallTelUri( @pcParametername = "uriString") final String uriString, @RpcParameter(name = "videoCall") @RpcOptional @RpcDefault("false") Boolean videoCall)358     public void telecomCallTelUri(
359             @RpcParameter(name = "uriString")
360     final String uriString,
361             @RpcParameter(name = "videoCall")
362             @RpcOptional
363             @RpcDefault("false")
364             Boolean videoCall) throws Exception {
365         if (!uriString.startsWith("tel:")) {
366             Log.w("Invalid tel URI" + uriString);
367             return;
368         }
369 
370         Intent intent = new Intent(Intent.ACTION_CALL);
371         intent.setDataAndType(Uri.parse(uriString).normalizeScheme(), null);
372 
373         if (videoCall) {
374             Log.d("Placing a bi-directional video call");
375             intent.putExtra(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE,
376                     VideoProfile.STATE_BIDIRECTIONAL);
377         }
378 
379         mAndroidFacade.startActivityIntent(intent, false);
380     }
381 
382     @Rpc(description = "Calls an Emergency number.")
telecomCallEmergencyNumber( @pcParametername = "number") final String number)383     public void telecomCallEmergencyNumber(
384                         @RpcParameter(name = "number")
385             final String number)
386             throws Exception {
387         String uriString = "tel:" + URLEncoder.encode(number, "ASCII");
388         mAndroidFacade.startActivity(Intent.ACTION_CALL_PRIVILEGED, uriString,
389                 null, null, null, null, null);
390     }
391 
392     @Rpc(description = "Dials a contact/phone number by URI.")
telecomDial( @pcParametername = "uri") final String uri)393     public void telecomDial(
394             @RpcParameter(name = "uri")
395     final String uri)
396             throws Exception {
397         mAndroidFacade.startActivity(Intent.ACTION_DIAL, uri, null, null, null,
398                 null, null);
399     }
400 
401     @Rpc(description = "Dials a phone number.")
telecomDialNumber(@pcParametername = "phone number") final String number)402     public void telecomDialNumber(@RpcParameter(name = "phone number")
403     final String number)
404             throws Exception, UnsupportedEncodingException {
405         telecomDial("tel:" + URLEncoder.encode(number, "ASCII"));
406     }
407 }
408