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