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.android.incallui.multisim; 18 19 import android.content.Context; 20 import android.net.Uri; 21 import android.os.Bundle; 22 import android.support.annotation.MainThread; 23 import android.support.annotation.NonNull; 24 import android.support.annotation.Nullable; 25 import android.support.annotation.VisibleForTesting; 26 import android.support.annotation.WorkerThread; 27 import android.telecom.PhoneAccount; 28 import android.telecom.PhoneAccountHandle; 29 import android.telecom.TelecomManager; 30 import com.android.dialer.common.Assert; 31 import com.android.dialer.common.LogUtil; 32 import com.android.dialer.common.concurrent.DialerExecutor.Worker; 33 import com.android.dialer.common.concurrent.ThreadUtil; 34 import com.android.dialer.preferredsim.suggestion.SimSuggestionComponent; 35 import com.android.dialer.util.PermissionsUtil; 36 import com.android.incallui.call.CallList; 37 import com.android.incallui.call.DialerCall; 38 import com.android.incallui.call.DialerCallListener; 39 import com.android.incallui.incalluilock.InCallUiLock; 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 /** 44 * Hangs up the current call and redial the call using the {@code otherAccount} instead. the in call 45 * ui will be prevented from closing until the process has finished. 46 */ 47 public class SwapSimWorker implements Worker<Void, Void>, DialerCallListener, CallList.Listener { 48 49 // Timeout waiting for the call to hangup or redial. 50 private static final int DEFAULT_TIMEOUT_MILLIS = 5_000; 51 52 private final Context context; 53 private final DialerCall call; 54 private final CallList callList; 55 private final InCallUiLock inCallUiLock; 56 57 private final CountDownLatch disconnectLatch = new CountDownLatch(1); 58 private final CountDownLatch dialingLatch = new CountDownLatch(1); 59 60 private final PhoneAccountHandle otherAccount; 61 private final String number; 62 63 private final int timeoutMillis; 64 65 private CountDownLatch latchForTest; 66 67 @MainThread SwapSimWorker( Context context, DialerCall call, CallList callList, PhoneAccountHandle otherAccount, InCallUiLock lock)68 public SwapSimWorker( 69 Context context, 70 DialerCall call, 71 CallList callList, 72 PhoneAccountHandle otherAccount, 73 InCallUiLock lock) { 74 this(context, call, callList, otherAccount, lock, DEFAULT_TIMEOUT_MILLIS); 75 } 76 77 @VisibleForTesting SwapSimWorker( Context context, DialerCall call, CallList callList, PhoneAccountHandle otherAccount, InCallUiLock lock, int timeoutMillis)78 SwapSimWorker( 79 Context context, 80 DialerCall call, 81 CallList callList, 82 PhoneAccountHandle otherAccount, 83 InCallUiLock lock, 84 int timeoutMillis) { 85 Assert.isMainThread(); 86 this.context = context; 87 this.call = call; 88 this.callList = callList; 89 this.otherAccount = otherAccount; 90 inCallUiLock = lock; 91 this.timeoutMillis = timeoutMillis; 92 number = call.getNumber(); 93 call.addListener(this); 94 call.disconnect(); 95 } 96 97 @WorkerThread 98 @Nullable 99 @Override 100 @SuppressWarnings("MissingPermission") doInBackground(Void unused)101 public Void doInBackground(Void unused) { 102 try { 103 SimSuggestionComponent.get(context) 104 .getSuggestionProvider() 105 .reportIncorrectSuggestion(context, number, otherAccount); 106 107 if (!PermissionsUtil.hasPhonePermissions(context)) { 108 LogUtil.e("SwapSimWorker.doInBackground", "missing phone permission"); 109 return null; 110 } 111 if (!disconnectLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)) { 112 LogUtil.e("SwapSimWorker.doInBackground", "timeout waiting for call to disconnect"); 113 return null; 114 } 115 LogUtil.i("SwapSimWorker.doInBackground", "call disconnected, redialing"); 116 TelecomManager telecomManager = context.getSystemService(TelecomManager.class); 117 Bundle extras = new Bundle(); 118 extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, otherAccount); 119 callList.addListener(this); 120 telecomManager.placeCall(Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null), extras); 121 if (latchForTest != null) { 122 latchForTest.countDown(); 123 } 124 if (!dialingLatch.await(timeoutMillis, TimeUnit.MILLISECONDS)) { 125 LogUtil.e("SwapSimWorker.doInBackground", "timeout waiting for call to dial"); 126 } 127 return null; 128 } catch (InterruptedException e) { 129 LogUtil.e("SwapSimWorker.doInBackground", "interrupted", e); 130 Thread.currentThread().interrupt(); 131 return null; 132 } finally { 133 ThreadUtil.postOnUiThread( 134 () -> { 135 call.removeListener(this); 136 callList.removeListener(this); 137 inCallUiLock.release(); 138 }); 139 } 140 } 141 142 @MainThread 143 @Override onDialerCallDisconnect()144 public void onDialerCallDisconnect() { 145 disconnectLatch.countDown(); 146 } 147 148 @Override onCallListChange(CallList callList)149 public void onCallListChange(CallList callList) { 150 if (callList.getOutgoingCall() != null) { 151 dialingLatch.countDown(); 152 } 153 } 154 155 @VisibleForTesting setLatchForTest(CountDownLatch latch)156 void setLatchForTest(CountDownLatch latch) { 157 latchForTest = latch; 158 } 159 160 @Override onDialerCallUpdate()161 public void onDialerCallUpdate() {} 162 163 @Override onDialerCallChildNumberChange()164 public void onDialerCallChildNumberChange() {} 165 166 @Override onDialerCallLastForwardedNumberChange()167 public void onDialerCallLastForwardedNumberChange() {} 168 169 @Override onDialerCallUpgradeToVideo()170 public void onDialerCallUpgradeToVideo() {} 171 172 @Override onDialerCallSessionModificationStateChange()173 public void onDialerCallSessionModificationStateChange() {} 174 175 @Override onWiFiToLteHandover()176 public void onWiFiToLteHandover() {} 177 178 @Override onHandoverToWifiFailure()179 public void onHandoverToWifiFailure() {} 180 181 @Override onInternationalCallOnWifi()182 public void onInternationalCallOnWifi() {} 183 184 @Override onEnrichedCallSessionUpdate()185 public void onEnrichedCallSessionUpdate() {} 186 187 @Override onIncomingCall(DialerCall call)188 public void onIncomingCall(DialerCall call) {} 189 190 @Override onUpgradeToVideo(DialerCall call)191 public void onUpgradeToVideo(DialerCall call) {} 192 193 @Override onSessionModificationStateChange(DialerCall call)194 public void onSessionModificationStateChange(DialerCall call) {} 195 196 @Override onDisconnect(DialerCall call)197 public void onDisconnect(DialerCall call) {} 198 199 @Override onWiFiToLteHandover(DialerCall call)200 public void onWiFiToLteHandover(DialerCall call) {} 201 202 @Override onHandoverToWifiFailed(DialerCall call)203 public void onHandoverToWifiFailed(DialerCall call) {} 204 205 @Override onInternationalCallOnWifi(@onNull DialerCall call)206 public void onInternationalCallOnWifi(@NonNull DialerCall call) {} 207 } 208