Home | History | Annotate | Download | only in sync
      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 
     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 
     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 
     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    */
     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 
    109   public NetworkRequest getNetworkRequest() {
    110     return networkRequest;
    111   }
    112 
    113   @Override
    114   @CallSuper
    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
    123   public void onAvailable(Network network) {
    124     super.onAvailable(network);
    125     resultReceived = true;
    126   }
    127 
    128   @CallSuper
    129   public void onUnavailable() {
    130     VvmLog.i(TAG, "onUnavailable");
    131     resultReceived = true;
    132     onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
    133   }
    134 
    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 
    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 
    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
    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