Home | History | Annotate | Download | only in telephony
      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 
     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
     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.")
     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.")
     98     public void telecomCancelMissedCallsNotification() {
     99         mTelecomManager.cancelMissedCallsNotification();
    100     }
    101 
    102     @Rpc(description = "Remove all Accounts that belong to the calling package from the system.")
    103     public void telecomClearAccounts() {
    104         mTelecomManager.clearAccounts();
    105     }
    106 
    107     @Rpc(description = "End an ongoing call.")
    108     public Boolean telecomEndCall() {
    109         return mTelecomManager.endCall();
    110     }
    111 
    112     @Rpc(description = "Get a list of all PhoneAccounts.")
    113     public List<PhoneAccount> telecomGetAllPhoneAccounts() {
    114         return mTelecomManager.getAllPhoneAccounts();
    115     }
    116 
    117     @Rpc(description = "Get the current call state.")
    118     public String telecomGetCallState() {
    119         int state = mTelecomManager.getCallState();
    120         return TelephonyUtils.getTelephonyCallStateString(state);
    121     }
    122 
    123     @Rpc(description = "Get the current tty mode.")
    124     public String telecomGetCurrentTtyMode() {
    125         int mode = mTelecomManager.getCurrentTtyMode();
    126         return TelephonyUtils.getTtyModeString(mode);
    127     }
    128 
    129     @Rpc(description = "Bring incallUI to foreground.")
    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.")
    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.")
    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.")
    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.")
    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.")
    200     public Boolean telecomIsInCall() {
    201         return mTelecomManager.isInCall();
    202     }
    203 
    204     @Rpc(description = "Returns whether there is a ringing incoming call.")
    205     public Boolean telecomIsRinging() {
    206         return mTelecomManager.isRinging();
    207     }
    208 
    209     @Rpc(description = "Silences the rigner if there's a ringing call.")
    210     public void telecomSilenceRinger() {
    211         mTelecomManager.silenceRinger();
    212     }
    213 
    214     @Rpc(description = "Swap two calls")
    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")
    220     public void telecomStartListeningForCallAdded() {
    221         InCallServiceImpl.CallListener.startListeningForEvent(
    222                 InCallServiceImpl.CallListener.LISTEN_CALL_ADDED);
    223     }
    224 
    225     @Rpc(description = "Stop listening for added calls")
    226     public void telecomStopListeningForCallAdded() {
    227         InCallServiceImpl.CallListener.stopListeningForEvent(
    228                 InCallServiceImpl.CallListener.LISTEN_CALL_ADDED);
    229     }
    230 
    231     @Rpc(description = "Start listening for removed calls")
    232     public void telecomStartListeningForCallRemoved() {
    233         InCallServiceImpl.CallListener.startListeningForEvent(
    234                 InCallServiceImpl.CallListener.LISTEN_CALL_REMOVED);
    235     }
    236 
    237     @Rpc(description = "Stop listening for removed calls")
    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.")
    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.")
    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")
    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.")
    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.")
    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.")
    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.")
    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.")
    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.")
    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.")
    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