1 /* 2 * Copyright (C) 2015 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.voicemail.impl.sync; 17 18 import android.annotation.TargetApi; 19 import android.content.Context; 20 import android.net.ConnectivityManager; 21 import android.net.Network; 22 import android.net.NetworkCapabilities; 23 import android.net.NetworkRequest; 24 import android.os.Build.VERSION_CODES; 25 import android.os.Handler; 26 import android.os.Looper; 27 import android.support.annotation.CallSuper; 28 import android.telecom.PhoneAccountHandle; 29 import android.telephony.TelephonyManager; 30 import com.android.dialer.common.Assert; 31 import com.android.voicemail.impl.OmtpEvents; 32 import com.android.voicemail.impl.OmtpVvmCarrierConfigHelper; 33 import com.android.voicemail.impl.VoicemailStatus; 34 import com.android.voicemail.impl.VvmLog; 35 36 /** 37 * Base class for network request call backs for visual voicemail syncing with the Imap server. This 38 * handles retries and network requests. 39 */ 40 @TargetApi(VERSION_CODES.O) 41 public abstract class VvmNetworkRequestCallback extends ConnectivityManager.NetworkCallback { 42 43 private static final String TAG = "VvmNetworkRequest"; 44 45 // Timeout used to call ConnectivityManager.requestNetwork 46 private static final int NETWORK_REQUEST_TIMEOUT_MILLIS = 60 * 1000; 47 48 public static final String NETWORK_REQUEST_FAILED_TIMEOUT = "timeout"; 49 public static final String NETWORK_REQUEST_FAILED_LOST = "lost"; 50 51 protected Context context; 52 protected PhoneAccountHandle phoneAccount; 53 protected NetworkRequest networkRequest; 54 private ConnectivityManager connectivityManager; 55 private final OmtpVvmCarrierConfigHelper carrierConfigHelper; 56 private final VoicemailStatus.Editor status; 57 private boolean requestSent = false; 58 private boolean resultReceived = false; 59 private boolean released = false; 60 VvmNetworkRequestCallback( Context context, PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status)61 public VvmNetworkRequestCallback( 62 Context context, PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status) { 63 this.context = context; 64 this.phoneAccount = phoneAccount; 65 this.status = status; 66 carrierConfigHelper = new OmtpVvmCarrierConfigHelper(context, this.phoneAccount); 67 networkRequest = createNetworkRequest(); 68 } 69 VvmNetworkRequestCallback( OmtpVvmCarrierConfigHelper config, PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status)70 public VvmNetworkRequestCallback( 71 OmtpVvmCarrierConfigHelper config, 72 PhoneAccountHandle phoneAccount, 73 VoicemailStatus.Editor status) { 74 context = config.getContext(); 75 this.phoneAccount = phoneAccount; 76 this.status = status; 77 carrierConfigHelper = config; 78 networkRequest = createNetworkRequest(); 79 } 80 getVoicemailStatusEditor()81 public VoicemailStatus.Editor getVoicemailStatusEditor() { 82 return status; 83 } 84 85 /** 86 * @return NetworkRequest for a proper transport type. Use only cellular network if the carrier 87 * requires it. Otherwise use whatever available. 88 */ createNetworkRequest()89 private NetworkRequest createNetworkRequest() { 90 91 NetworkRequest.Builder builder = 92 new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); 93 94 TelephonyManager telephonyManager = 95 context.getSystemService(TelephonyManager.class).createForPhoneAccountHandle(phoneAccount); 96 // At this point mPhoneAccount should always be valid and telephonyManager will never be null 97 Assert.isNotNull(telephonyManager); 98 if (carrierConfigHelper.isCellularDataRequired()) { 99 VvmLog.d(TAG, "Transport type: CELLULAR"); 100 builder 101 .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) 102 .setNetworkSpecifier(telephonyManager.getNetworkSpecifier()); 103 } else { 104 VvmLog.d(TAG, "Transport type: ANY"); 105 } 106 return builder.build(); 107 } 108 getNetworkRequest()109 public NetworkRequest getNetworkRequest() { 110 return networkRequest; 111 } 112 113 @Override 114 @CallSuper onLost(Network network)115 public void onLost(Network network) { 116 VvmLog.i(TAG, "onLost"); 117 resultReceived = true; 118 onFailed(NETWORK_REQUEST_FAILED_LOST); 119 } 120 121 @Override 122 @CallSuper onAvailable(Network network)123 public void onAvailable(Network network) { 124 super.onAvailable(network); 125 resultReceived = true; 126 } 127 128 @CallSuper onUnavailable()129 public void onUnavailable() { 130 VvmLog.i(TAG, "onUnavailable"); 131 resultReceived = true; 132 onFailed(NETWORK_REQUEST_FAILED_TIMEOUT); 133 } 134 requestNetwork()135 public void requestNetwork() { 136 if (requestSent == true) { 137 VvmLog.e(TAG, "requestNetwork() called twice"); 138 return; 139 } 140 requestSent = true; 141 getConnectivityManager().requestNetwork(getNetworkRequest(), this); 142 /** 143 * Somehow requestNetwork() with timeout doesn't work, and it's a hidden method. Implement our 144 * own timeout mechanism instead. 145 */ 146 Handler handler = new Handler(Looper.getMainLooper()); 147 handler.postDelayed( 148 new Runnable() { 149 @Override 150 public void run() { 151 if (resultReceived == false) { 152 onFailed(NETWORK_REQUEST_FAILED_TIMEOUT); 153 } 154 } 155 }, 156 NETWORK_REQUEST_TIMEOUT_MILLIS); 157 } 158 releaseNetwork()159 public void releaseNetwork() { 160 VvmLog.i(TAG, "releaseNetwork"); 161 if (!released) { 162 getConnectivityManager().unregisterNetworkCallback(this); 163 released = true; 164 } else { 165 VvmLog.w(TAG, "already released"); 166 } 167 } 168 getConnectivityManager()169 public ConnectivityManager getConnectivityManager() { 170 if (connectivityManager == null) { 171 connectivityManager = 172 (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); 173 } 174 return connectivityManager; 175 } 176 177 @CallSuper onFailed(String reason)178 public void onFailed(String reason) { 179 VvmLog.i(TAG, "onFailed: " + reason); 180 if (carrierConfigHelper.isCellularDataRequired()) { 181 carrierConfigHelper.handleEvent(status, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED); 182 } else { 183 carrierConfigHelper.handleEvent(status, OmtpEvents.DATA_NO_CONNECTION); 184 } 185 releaseNetwork(); 186 } 187 } 188