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