blob: 75aac106e0e0d4e64ec86f8e6254f5878c6854af [file] [log] [blame]
markchienb6eb2c22018-07-18 14:29:20 +08001/*
2 * Copyright (C) 2018 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
17package com.android.server.connectivity.tethering;
18
19import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
20import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
21import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
22import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
23import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
markchienf2731272019-01-16 17:44:13 +080024import static android.net.ConnectivityManager.TETHER_ERROR_ENTITLEMENT_UNKONWN;
25import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
26import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
markchienb6eb2c22018-07-18 14:29:20 +080027
28import static com.android.internal.R.string.config_wifi_tether_enable;
29
30import android.annotation.Nullable;
31import android.content.ComponentName;
32import android.content.Context;
33import android.content.Intent;
34import android.content.res.Resources;
35import android.net.util.SharedLog;
36import android.os.Binder;
markchienf2731272019-01-16 17:44:13 +080037import android.os.Bundle;
38import android.os.Handler;
39import android.os.Parcel;
markchienb6eb2c22018-07-18 14:29:20 +080040import android.os.PersistableBundle;
41import android.os.ResultReceiver;
42import android.os.UserHandle;
43import android.provider.Settings;
44import android.telephony.CarrierConfigManager;
45import android.util.ArraySet;
markchienf2731272019-01-16 17:44:13 +080046import android.util.Log;
47import android.util.SparseIntArray;
markchienb6eb2c22018-07-18 14:29:20 +080048
49import com.android.internal.annotations.GuardedBy;
50import com.android.internal.annotations.VisibleForTesting;
markchienf2731272019-01-16 17:44:13 +080051import com.android.internal.util.StateMachine;
markchienb6eb2c22018-07-18 14:29:20 +080052import com.android.server.connectivity.MockableSystemProperties;
53
54/**
55 * This class encapsulates entitlement/provisioning mechanics
56 * provisioning check only applies to the use of the mobile network as an upstream
57 *
58 * @hide
59 */
60public class EntitlementManager {
61 private static final String TAG = EntitlementManager.class.getSimpleName();
markchienf2731272019-01-16 17:44:13 +080062 private static final boolean DBG = false;
markchienb6eb2c22018-07-18 14:29:20 +080063
64 // {@link ComponentName} of the Service used to run tether provisioning.
65 private static final ComponentName TETHER_SERVICE = ComponentName.unflattenFromString(
66 Resources.getSystem().getString(config_wifi_tether_enable));
67 protected static final String DISABLE_PROVISIONING_SYSPROP_KEY = "net.tethering.noprovisioning";
68
69 // The ArraySet contains enabled downstream types, ex:
70 // {@link ConnectivityManager.TETHERING_WIFI}
71 // {@link ConnectivityManager.TETHERING_USB}
72 // {@link ConnectivityManager.TETHERING_BLUETOOTH}
73 @GuardedBy("mCurrentTethers")
74 private final ArraySet<Integer> mCurrentTethers;
75 private final Context mContext;
76 private final MockableSystemProperties mSystemProperties;
77 private final SharedLog mLog;
markchienf2731272019-01-16 17:44:13 +080078 private final Handler mMasterHandler;
79 private final SparseIntArray mEntitlementCacheValue;
markchienb6eb2c22018-07-18 14:29:20 +080080 @Nullable
81 private TetheringConfiguration mConfig;
82
markchienf2731272019-01-16 17:44:13 +080083 public EntitlementManager(Context ctx, StateMachine tetherMasterSM, SharedLog log,
markchienb6eb2c22018-07-18 14:29:20 +080084 MockableSystemProperties systemProperties) {
85 mContext = ctx;
markchienf2731272019-01-16 17:44:13 +080086 mLog = log.forSubComponent(TAG);
markchienb6eb2c22018-07-18 14:29:20 +080087 mCurrentTethers = new ArraySet<Integer>();
88 mSystemProperties = systemProperties;
markchienf2731272019-01-16 17:44:13 +080089 mEntitlementCacheValue = new SparseIntArray();
90 mMasterHandler = tetherMasterSM.getHandler();
markchienb6eb2c22018-07-18 14:29:20 +080091 }
92
93 /**
94 * Pass a new TetheringConfiguration instance each time when
95 * Tethering#updateConfiguration() is called.
96 */
97 public void updateConfiguration(TetheringConfiguration conf) {
98 mConfig = conf;
99 }
100
101 /**
102 * Tell EntitlementManager that a given type of tethering has been enabled
103 *
104 * @param type Tethering type
105 */
106 public void startTethering(int type) {
107 synchronized (mCurrentTethers) {
108 mCurrentTethers.add(type);
109 }
110 }
111
112 /**
113 * Tell EntitlementManager that a given type of tethering has been disabled
114 *
115 * @param type Tethering type
116 */
117 public void stopTethering(int type) {
118 synchronized (mCurrentTethers) {
119 mCurrentTethers.remove(type);
120 }
121 }
122
123 /**
124 * Check if the device requires a provisioning check in order to enable tethering.
125 *
126 * @return a boolean - {@code true} indicating tether provisioning is required by the carrier.
127 */
128 @VisibleForTesting
129 public boolean isTetherProvisioningRequired() {
130 if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
131 || mConfig.provisioningApp.length == 0) {
132 return false;
133 }
134 if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
135 return false;
136 }
137 return (mConfig.provisioningApp.length == 2);
138 }
139
140 /**
141 * Re-check tethering provisioning for enabled downstream tether types.
142 * Reference ConnectivityManager.TETHERING_{@code *} for each tether type.
143 */
144 public void reevaluateSimCardProvisioning() {
markchienf2731272019-01-16 17:44:13 +0800145 synchronized (mEntitlementCacheValue) {
146 mEntitlementCacheValue.clear();
147 }
148
markchienb6eb2c22018-07-18 14:29:20 +0800149 if (!mConfig.hasMobileHotspotProvisionApp()) return;
150 if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
151
152 final ArraySet<Integer> reevaluateType;
153 synchronized (mCurrentTethers) {
154 reevaluateType = new ArraySet<Integer>(mCurrentTethers);
155 }
156 for (Integer type : reevaluateType) {
157 startProvisionIntent(type);
158 }
159 }
160
161 // The logic here is aimed solely at confirming that a CarrierConfig exists
162 // and affirms that entitlement checks are not required.
163 //
164 // TODO: find a better way to express this, or alter the checking process
165 // entirely so that this is more intuitive.
166 private boolean carrierConfigAffirmsEntitlementCheckNotRequired() {
167 // Check carrier config for entitlement checks
168 final CarrierConfigManager configManager = (CarrierConfigManager) mContext
169 .getSystemService(Context.CARRIER_CONFIG_SERVICE);
170 if (configManager == null) return false;
171
172 final PersistableBundle carrierConfig = configManager.getConfig();
173 if (carrierConfig == null) return false;
174
175 // A CarrierConfigManager was found and it has a config.
176 final boolean isEntitlementCheckRequired = carrierConfig.getBoolean(
177 CarrierConfigManager.KEY_REQUIRE_ENTITLEMENT_CHECKS_BOOL);
178 return !isEntitlementCheckRequired;
179 }
180
181 public void runSilentTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
182 Intent intent = new Intent();
183 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
184 intent.putExtra(EXTRA_RUN_PROVISION, true);
185 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
186 intent.setComponent(TETHER_SERVICE);
187 final long ident = Binder.clearCallingIdentity();
188 try {
189 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
190 } finally {
191 Binder.restoreCallingIdentity(ident);
192 }
193 }
194
195 public void runUiTetherProvisioningAndEnable(int type, ResultReceiver receiver) {
markchienf2731272019-01-16 17:44:13 +0800196 runUiTetherProvisioning(type, receiver);
197 }
198
199 @VisibleForTesting
200 protected void runUiTetherProvisioning(int type, ResultReceiver receiver) {
markchienb6eb2c22018-07-18 14:29:20 +0800201 Intent intent = new Intent(Settings.ACTION_TETHER_PROVISIONING);
202 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
203 intent.putExtra(EXTRA_PROVISION_CALLBACK, receiver);
204 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
205 final long ident = Binder.clearCallingIdentity();
206 try {
207 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
208 } finally {
209 Binder.restoreCallingIdentity(ident);
210 }
211 }
212
213 // Used by the SIM card change observation code.
214 // TODO: De-duplicate with above code, where possible.
215 private void startProvisionIntent(int tetherType) {
216 final Intent startProvIntent = new Intent();
217 startProvIntent.putExtra(EXTRA_ADD_TETHER_TYPE, tetherType);
218 startProvIntent.putExtra(EXTRA_RUN_PROVISION, true);
219 startProvIntent.setComponent(TETHER_SERVICE);
220 mContext.startServiceAsUser(startProvIntent, UserHandle.CURRENT);
221 }
222
223 public void scheduleProvisioningRechecks(int type) {
224 Intent intent = new Intent();
225 intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
226 intent.putExtra(EXTRA_SET_ALARM, true);
227 intent.setComponent(TETHER_SERVICE);
228 final long ident = Binder.clearCallingIdentity();
229 try {
230 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
231 } finally {
232 Binder.restoreCallingIdentity(ident);
233 }
234 }
235
236 public void cancelTetherProvisioningRechecks(int type) {
237 Intent intent = new Intent();
238 intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
239 intent.setComponent(TETHER_SERVICE);
240 final long ident = Binder.clearCallingIdentity();
241 try {
242 mContext.startServiceAsUser(intent, UserHandle.CURRENT);
243 } finally {
244 Binder.restoreCallingIdentity(ident);
245 }
246 }
markchienf2731272019-01-16 17:44:13 +0800247
248 private ResultReceiver buildProxyReceiver(int type, final ResultReceiver receiver) {
249 ResultReceiver rr = new ResultReceiver(mMasterHandler) {
250 @Override
251 protected void onReceiveResult(int resultCode, Bundle resultData) {
252 int updatedCacheValue = updateEntitlementCacheValue(type, resultCode);
253 receiver.send(updatedCacheValue, null);
254 }
255 };
256
257 return writeToParcel(rr);
258 }
259
260 private ResultReceiver writeToParcel(final ResultReceiver receiver) {
261 // This is necessary to avoid unmarshalling issues when sending the receiver
262 // across processes.
263 Parcel parcel = Parcel.obtain();
264 receiver.writeToParcel(parcel, 0);
265 parcel.setDataPosition(0);
266 ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
267 parcel.recycle();
268 return receiverForSending;
269 }
270
271 /**
272 * Update the last entitlement value to internal cache
273 *
274 * @param type tethering type from ConnectivityManager.TETHERING_{@code *}
275 * @param resultCode last entitlement value
276 * @return the last updated entitlement value
277 */
278 public int updateEntitlementCacheValue(int type, int resultCode) {
279 if (DBG) {
280 Log.d(TAG, "updateEntitlementCacheValue: " + type + ", result: " + resultCode);
281 }
282 synchronized (mEntitlementCacheValue) {
283 if (resultCode == TETHER_ERROR_NO_ERROR) {
284 mEntitlementCacheValue.put(type, resultCode);
285 return resultCode;
286 } else {
287 mEntitlementCacheValue.put(type, TETHER_ERROR_PROVISION_FAILED);
288 return TETHER_ERROR_PROVISION_FAILED;
289 }
290 }
291 }
292
293 /** Get the last value of the tethering entitlement check. */
294 public void getLatestTetheringEntitlementValue(int downstream, ResultReceiver receiver,
295 boolean showEntitlementUi) {
296 if (!isTetherProvisioningRequired()) {
297 receiver.send(TETHER_ERROR_NO_ERROR, null);
298 return;
299 }
300
301 final int cacheValue;
302 synchronized (mEntitlementCacheValue) {
303 cacheValue = mEntitlementCacheValue.get(
304 downstream, TETHER_ERROR_ENTITLEMENT_UNKONWN);
305 }
306 if (cacheValue == TETHER_ERROR_NO_ERROR || !showEntitlementUi) {
307 receiver.send(cacheValue, null);
308 } else {
309 ResultReceiver proxy = buildProxyReceiver(downstream, receiver);
310 runUiTetherProvisioning(downstream, proxy);
311 }
312 }
markchienb6eb2c22018-07-18 14:29:20 +0800313}