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 mContext;
     52   protected PhoneAccountHandle mPhoneAccount;
     53   protected NetworkRequest mNetworkRequest;
     54   private ConnectivityManager mConnectivityManager;
     55   private final OmtpVvmCarrierConfigHelper mCarrierConfigHelper;
     56   private final VoicemailStatus.Editor mStatus;
     57   private boolean mRequestSent = false;
     58   private boolean mResultReceived = false;
     59 
     60   public VvmNetworkRequestCallback(
     61       Context context, PhoneAccountHandle phoneAccount, VoicemailStatus.Editor status) {
     62     mContext = context;
     63     mPhoneAccount = phoneAccount;
     64     mStatus = status;
     65     mCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(context, mPhoneAccount);
     66     mNetworkRequest = createNetworkRequest();
     67   }
     68 
     69   public VvmNetworkRequestCallback(
     70       OmtpVvmCarrierConfigHelper config,
     71       PhoneAccountHandle phoneAccount,
     72       VoicemailStatus.Editor status) {
     73     mContext = config.getContext();
     74     mPhoneAccount = phoneAccount;
     75     mStatus = status;
     76     mCarrierConfigHelper = config;
     77     mNetworkRequest = createNetworkRequest();
     78   }
     79 
     80   public VoicemailStatus.Editor getVoicemailStatusEditor() {
     81     return mStatus;
     82   }
     83 
     84   /**
     85    * @return NetworkRequest for a proper transport type. Use only cellular network if the carrier
     86    *     requires it. Otherwise use whatever available.
     87    */
     88   private NetworkRequest createNetworkRequest() {
     89 
     90     NetworkRequest.Builder builder =
     91         new NetworkRequest.Builder().addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
     92 
     93     TelephonyManager telephonyManager =
     94         mContext
     95             .getSystemService(TelephonyManager.class)
     96             .createForPhoneAccountHandle(mPhoneAccount);
     97     // At this point mPhoneAccount should always be valid and telephonyManager will never be null
     98     Assert.isNotNull(telephonyManager);
     99     if (mCarrierConfigHelper.isCellularDataRequired()) {
    100       VvmLog.d(TAG, "Transport type: CELLULAR");
    101       builder
    102           .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
    103           .setNetworkSpecifier(telephonyManager.getNetworkSpecifier());
    104     } else {
    105       VvmLog.d(TAG, "Transport type: ANY");
    106     }
    107     return builder.build();
    108   }
    109 
    110   public NetworkRequest getNetworkRequest() {
    111     return mNetworkRequest;
    112   }
    113 
    114   @Override
    115   @CallSuper
    116   public void onLost(Network network) {
    117     VvmLog.d(TAG, "onLost");
    118     mResultReceived = true;
    119     onFailed(NETWORK_REQUEST_FAILED_LOST);
    120   }
    121 
    122   @Override
    123   @CallSuper
    124   public void onAvailable(Network network) {
    125     super.onAvailable(network);
    126     mResultReceived = true;
    127   }
    128 
    129   @CallSuper
    130   public void onUnavailable() {
    131     // TODO: b/32637799 this is hidden, do we really need this?
    132     mResultReceived = true;
    133     onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
    134   }
    135 
    136   public void requestNetwork() {
    137     if (mRequestSent == true) {
    138       VvmLog.e(TAG, "requestNetwork() called twice");
    139       return;
    140     }
    141     mRequestSent = true;
    142     getConnectivityManager().requestNetwork(getNetworkRequest(), this);
    143     /**
    144      * Somehow requestNetwork() with timeout doesn't work, and it's a hidden method. Implement our
    145      * own timeout mechanism instead.
    146      */
    147     Handler handler = new Handler(Looper.getMainLooper());
    148     handler.postDelayed(
    149         new Runnable() {
    150           @Override
    151           public void run() {
    152             if (mResultReceived == false) {
    153               onFailed(NETWORK_REQUEST_FAILED_TIMEOUT);
    154             }
    155           }
    156         },
    157         NETWORK_REQUEST_TIMEOUT_MILLIS);
    158   }
    159 
    160   public void releaseNetwork() {
    161     VvmLog.d(TAG, "releaseNetwork");
    162     getConnectivityManager().unregisterNetworkCallback(this);
    163   }
    164 
    165   public ConnectivityManager getConnectivityManager() {
    166     if (mConnectivityManager == null) {
    167       mConnectivityManager =
    168           (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
    169     }
    170     return mConnectivityManager;
    171   }
    172 
    173   @CallSuper
    174   public void onFailed(String reason) {
    175     VvmLog.d(TAG, "onFailed: " + reason);
    176     if (mCarrierConfigHelper.isCellularDataRequired()) {
    177       mCarrierConfigHelper.handleEvent(mStatus, OmtpEvents.DATA_NO_CONNECTION_CELLULAR_REQUIRED);
    178     } else {
    179       mCarrierConfigHelper.handleEvent(mStatus, OmtpEvents.DATA_NO_CONNECTION);
    180     }
    181     releaseNetwork();
    182   }
    183 }
    184