1 /* 2 * Copyright (C) 2008 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 17 package com.android.settings.wifi.tether; 18 19 import static android.net.TetheringConstants.EXTRA_ADD_TETHER_TYPE; 20 import static android.net.TetheringConstants.EXTRA_PROVISION_CALLBACK; 21 import static android.net.TetheringConstants.EXTRA_REM_TETHER_TYPE; 22 import static android.net.TetheringConstants.EXTRA_RUN_PROVISION; 23 import static android.net.TetheringManager.TETHERING_BLUETOOTH; 24 import static android.net.TetheringManager.TETHERING_ETHERNET; 25 import static android.net.TetheringManager.TETHERING_INVALID; 26 import static android.net.TetheringManager.TETHERING_USB; 27 import static android.net.TetheringManager.TETHERING_WIFI; 28 import static android.net.TetheringManager.TETHER_ERROR_NO_ERROR; 29 import static android.net.TetheringManager.TETHER_ERROR_PROVISIONING_FAILED; 30 import static android.net.TetheringManager.TETHER_ERROR_UNKNOWN_IFACE; 31 import static android.telephony.SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX; 32 import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID; 33 34 import android.app.Activity; 35 import android.app.Service; 36 import android.app.usage.UsageStatsManager; 37 import android.content.BroadcastReceiver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.SharedPreferences; 42 import android.content.pm.PackageManager; 43 import android.content.pm.ResolveInfo; 44 import android.net.TetheringManager; 45 import android.os.IBinder; 46 import android.os.ResultReceiver; 47 import android.telephony.SubscriptionManager; 48 import android.text.TextUtils; 49 import android.util.ArrayMap; 50 import android.util.Log; 51 52 import androidx.annotation.VisibleForTesting; 53 54 import java.util.ArrayList; 55 import java.util.List; 56 import java.util.Objects; 57 58 public class TetherService extends Service { 59 private static final String TAG = "TetherService"; 60 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 61 62 @VisibleForTesting 63 public static final String EXTRA_RESULT = "EntitlementResult"; 64 @VisibleForTesting 65 public static final String EXTRA_TETHER_SUBID = "android.net.extra.TETHER_SUBID"; 66 @VisibleForTesting 67 public static final String EXTRA_TETHER_PROVISIONING_RESPONSE = 68 "android.net.extra.TETHER_PROVISIONING_RESPONSE"; 69 @VisibleForTesting 70 public static final String EXTRA_TETHER_SILENT_PROVISIONING_ACTION = 71 "android.net.extra.TETHER_SILENT_PROVISIONING_ACTION"; 72 73 // Activity results to match the activity provision protocol. 74 // Default to something not ok. 75 private static final int RESULT_DEFAULT = Activity.RESULT_CANCELED; 76 private static final int RESULT_OK = Activity.RESULT_OK; 77 78 private static final String TETHER_CHOICE = "TETHER_TYPE"; 79 private static final int MS_PER_HOUR = 60 * 60 * 1000; 80 81 private static final String PREFS = "tetherPrefs"; 82 private static final String KEY_TETHERS = "currentTethers"; 83 84 private int mCurrentTypeIndex; 85 private boolean mInProvisionCheck; 86 /** Intent action received from the provisioning app when entitlement check completes. */ 87 private String mExpectedProvisionResponseAction = null; 88 /** Intent action sent to the provisioning app to request an entitlement check. */ 89 private String mProvisionAction; 90 private int mSubId = INVALID_SUBSCRIPTION_ID; 91 private TetherServiceWrapper mWrapper; 92 private ArrayList<Integer> mCurrentTethers; 93 private ArrayMap<Integer, List<ResultReceiver>> mPendingCallbacks; 94 95 @Override onBind(Intent intent)96 public IBinder onBind(Intent intent) { 97 return null; 98 } 99 100 @Override onCreate()101 public void onCreate() { 102 super.onCreate(); 103 if (DEBUG) Log.d(TAG, "Creating TetherService"); 104 SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); 105 mCurrentTethers = stringToTethers(prefs.getString(KEY_TETHERS, "")); 106 mCurrentTypeIndex = 0; 107 mPendingCallbacks = new ArrayMap<>(3); 108 mPendingCallbacks.put(TETHERING_WIFI, new ArrayList<ResultReceiver>()); 109 mPendingCallbacks.put(TETHERING_USB, new ArrayList<ResultReceiver>()); 110 mPendingCallbacks.put(TETHERING_BLUETOOTH, new ArrayList<ResultReceiver>()); 111 mPendingCallbacks.put(TETHERING_ETHERNET, new ArrayList<ResultReceiver>()); 112 } 113 114 // Registers the broadcast receiver for the specified response action, first unregistering 115 // the receiver if it was registered for a different response action. maybeRegisterReceiver(final String responseAction)116 private void maybeRegisterReceiver(final String responseAction) { 117 if (Objects.equals(responseAction, mExpectedProvisionResponseAction)) return; 118 119 if (mExpectedProvisionResponseAction != null) unregisterReceiver(mReceiver); 120 121 registerReceiver(mReceiver, new IntentFilter(responseAction), 122 android.Manifest.permission.TETHER_PRIVILEGED, null /* handler */, 123 Context.RECEIVER_EXPORTED); 124 mExpectedProvisionResponseAction = responseAction; 125 if (DEBUG) Log.d(TAG, "registerReceiver " + responseAction); 126 } 127 stopSelfAndStartNotSticky()128 private int stopSelfAndStartNotSticky() { 129 stopSelf(); 130 return START_NOT_STICKY; 131 } 132 133 @Override onStartCommand(Intent intent, int flags, int startId)134 public int onStartCommand(Intent intent, int flags, int startId) { 135 if (intent.hasExtra(EXTRA_TETHER_SUBID)) { 136 final int tetherSubId = intent.getIntExtra(EXTRA_TETHER_SUBID, INVALID_SUBSCRIPTION_ID); 137 final int subId = getTetherServiceWrapper().getActiveDataSubscriptionId(); 138 if (tetherSubId != subId) { 139 Log.e(TAG, "This Provisioning request is outdated, current subId: " + subId); 140 if (!mInProvisionCheck) { 141 stopSelf(); 142 } 143 return START_NOT_STICKY; 144 } 145 mSubId = subId; 146 } 147 148 if (intent.hasExtra(EXTRA_ADD_TETHER_TYPE)) { 149 int type = intent.getIntExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_INVALID); 150 ResultReceiver callback = intent.getParcelableExtra(EXTRA_PROVISION_CALLBACK); 151 if (callback != null) { 152 List<ResultReceiver> callbacksForType = mPendingCallbacks.get(type); 153 if (callbacksForType != null) { 154 callbacksForType.add(callback); 155 } else { 156 // Invalid tether type. Just ignore this request and report failure. 157 Log.e(TAG, "Invalid tethering type " + type + ", stopping"); 158 callback.send(TETHER_ERROR_UNKNOWN_IFACE, null); 159 return stopSelfAndStartNotSticky(); 160 } 161 } 162 163 if (!mCurrentTethers.contains(type)) { 164 if (DEBUG) Log.d(TAG, "Adding tether " + type); 165 mCurrentTethers.add(type); 166 } 167 } 168 169 mProvisionAction = intent.getStringExtra(EXTRA_TETHER_SILENT_PROVISIONING_ACTION); 170 if (mProvisionAction == null) { 171 Log.e(TAG, "null provisioning action, stop "); 172 return stopSelfAndStartNotSticky(); 173 } 174 175 final String response = intent.getStringExtra(EXTRA_TETHER_PROVISIONING_RESPONSE); 176 if (response == null) { 177 Log.e(TAG, "null provisioning response, stop "); 178 return stopSelfAndStartNotSticky(); 179 } 180 maybeRegisterReceiver(response); 181 182 if (intent.hasExtra(EXTRA_REM_TETHER_TYPE)) { 183 if (!mInProvisionCheck) { 184 int type = intent.getIntExtra(EXTRA_REM_TETHER_TYPE, TETHERING_INVALID); 185 int index = mCurrentTethers.indexOf(type); 186 if (DEBUG) Log.d(TAG, "Removing tether " + type + ", index " + index); 187 if (index >= 0) { 188 removeTypeAtIndex(index); 189 } 190 } else { 191 if (DEBUG) Log.d(TAG, "Don't remove tether type during provisioning"); 192 } 193 } 194 195 if (intent.getBooleanExtra(EXTRA_RUN_PROVISION, false)) { 196 startProvisioning(mCurrentTypeIndex); 197 } else if (!mInProvisionCheck) { 198 // If we aren't running any provisioning, no reason to stay alive. 199 if (DEBUG) Log.d(TAG, "Stopping self. startid: " + startId); 200 return stopSelfAndStartNotSticky(); 201 } 202 // We want to be started if we are killed accidently, so that we can be sure we finish 203 // the check. 204 return START_REDELIVER_INTENT; 205 } 206 207 @Override onDestroy()208 public void onDestroy() { 209 if (mInProvisionCheck) { 210 Log.e(TAG, "TetherService getting destroyed while mid-provisioning" 211 + mCurrentTethers.get(mCurrentTypeIndex)); 212 } 213 SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); 214 prefs.edit().putString(KEY_TETHERS, tethersToString(mCurrentTethers)).commit(); 215 216 if (mExpectedProvisionResponseAction != null) { 217 unregisterReceiver(mReceiver); 218 mExpectedProvisionResponseAction = null; 219 } 220 if (DEBUG) Log.d(TAG, "Destroying TetherService"); 221 super.onDestroy(); 222 } 223 removeTypeAtIndex(int index)224 private void removeTypeAtIndex(int index) { 225 mCurrentTethers.remove(index); 226 // If we are currently in the middle of a check, we may need to adjust the 227 // index accordingly. 228 if (DEBUG) Log.d(TAG, "mCurrentTypeIndex: " + mCurrentTypeIndex); 229 if (index <= mCurrentTypeIndex && mCurrentTypeIndex > 0) { 230 mCurrentTypeIndex--; 231 } 232 } 233 stringToTethers(String tethersStr)234 private ArrayList<Integer> stringToTethers(String tethersStr) { 235 ArrayList<Integer> ret = new ArrayList<Integer>(); 236 if (TextUtils.isEmpty(tethersStr)) return ret; 237 238 String[] tethersSplit = tethersStr.split(","); 239 for (int i = 0; i < tethersSplit.length; i++) { 240 ret.add(Integer.parseInt(tethersSplit[i])); 241 } 242 return ret; 243 } 244 tethersToString(ArrayList<Integer> tethers)245 private String tethersToString(ArrayList<Integer> tethers) { 246 final StringBuffer buffer = new StringBuffer(); 247 final int N = tethers.size(); 248 for (int i = 0; i < N; i++) { 249 if (i != 0) { 250 buffer.append(','); 251 } 252 buffer.append(tethers.get(i)); 253 } 254 255 return buffer.toString(); 256 } 257 disableTethering(final int tetheringType)258 private void disableTethering(final int tetheringType) { 259 Log.w(TAG, "Disable tethering, type:" + tetheringType); 260 final TetheringManager tm = (TetheringManager) getSystemService(Context.TETHERING_SERVICE); 261 tm.stopTethering(tetheringType); 262 } 263 startProvisioning(int index)264 private void startProvisioning(int index) { 265 if (index >= mCurrentTethers.size()) return; 266 267 Intent intent = getProvisionBroadcastIntent(index); 268 setEntitlementAppActive(index); 269 270 if (DEBUG) { 271 Log.d(TAG, "Sending provisioning broadcast: " + intent.getAction() 272 + " type: " + mCurrentTethers.get(index)); 273 } 274 275 sendBroadcast(intent); 276 mInProvisionCheck = true; 277 } 278 getProvisionBroadcastIntent(int index)279 private Intent getProvisionBroadcastIntent(int index) { 280 if (mProvisionAction == null) Log.wtf(TAG, "null provisioning action"); 281 Intent intent = new Intent(mProvisionAction); 282 int type = mCurrentTethers.get(index); 283 intent.putExtra(TETHER_CHOICE, type); 284 intent.putExtra(EXTRA_SUBSCRIPTION_INDEX, mSubId); 285 intent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND 286 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 287 288 return intent; 289 } 290 setEntitlementAppActive(int index)291 private void setEntitlementAppActive(int index) { 292 final PackageManager packageManager = getPackageManager(); 293 Intent intent = getProvisionBroadcastIntent(index); 294 List<ResolveInfo> resolvers = 295 packageManager.queryBroadcastReceivers(intent, PackageManager.MATCH_ALL); 296 if (resolvers.isEmpty()) { 297 Log.e(TAG, "No found BroadcastReceivers for provision intent."); 298 return; 299 } 300 301 for (ResolveInfo resolver : resolvers) { 302 if (resolver.activityInfo.applicationInfo.isSystemApp()) { 303 String packageName = resolver.activityInfo.packageName; 304 getTetherServiceWrapper().setAppInactive(packageName, false); 305 } 306 } 307 } 308 fireCallbacksForType(int type, int result)309 private void fireCallbacksForType(int type, int result) { 310 List<ResultReceiver> callbacksForType = mPendingCallbacks.get(type); 311 if (callbacksForType == null) { 312 return; 313 } 314 int errorCode = result == RESULT_OK ? TETHER_ERROR_NO_ERROR : 315 TETHER_ERROR_PROVISIONING_FAILED; 316 for (ResultReceiver callback : callbacksForType) { 317 if (DEBUG) Log.d(TAG, "Firing result: " + errorCode + " to callback"); 318 callback.send(errorCode, null); 319 } 320 callbacksForType.clear(); 321 } 322 323 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 324 @Override 325 public void onReceive(Context context, Intent intent) { 326 if (DEBUG) Log.d(TAG, "Got provision result " + intent); 327 328 if (!intent.getAction().equals(mExpectedProvisionResponseAction)) { 329 Log.e(TAG, "Received provisioning response for unexpected action=" 330 + intent.getAction() + ", expected=" + mExpectedProvisionResponseAction); 331 return; 332 } 333 334 if (!mInProvisionCheck) { 335 Log.e(TAG, "Unexpected provisioning response when not in provisioning check" 336 + intent); 337 return; 338 } 339 int checkType = mCurrentTethers.get(mCurrentTypeIndex); 340 mInProvisionCheck = false; 341 int result = intent.getIntExtra(EXTRA_RESULT, RESULT_DEFAULT); 342 if (result != RESULT_OK) disableTethering(checkType); 343 fireCallbacksForType(checkType, result); 344 345 if (++mCurrentTypeIndex >= mCurrentTethers.size()) { 346 // We are done with all checks, time to die. 347 stopSelf(); 348 } else { 349 // Start the next check in our list. 350 startProvisioning(mCurrentTypeIndex); 351 } 352 } 353 }; 354 355 @VisibleForTesting setTetherServiceWrapper(TetherServiceWrapper wrapper)356 void setTetherServiceWrapper(TetherServiceWrapper wrapper) { 357 mWrapper = wrapper; 358 } 359 getTetherServiceWrapper()360 private TetherServiceWrapper getTetherServiceWrapper() { 361 if (mWrapper == null) { 362 mWrapper = new TetherServiceWrapper(this); 363 } 364 return mWrapper; 365 } 366 367 /** 368 * A static helper class used for tests. UsageStatsManager cannot be mocked out because 369 * it's marked final. This class can be mocked out instead. 370 */ 371 @VisibleForTesting 372 public static class TetherServiceWrapper { 373 private final UsageStatsManager mUsageStatsManager; 374 TetherServiceWrapper(Context context)375 TetherServiceWrapper(Context context) { 376 mUsageStatsManager = (UsageStatsManager) 377 context.getSystemService(Context.USAGE_STATS_SERVICE); 378 } 379 setAppInactive(String packageName, boolean isInactive)380 void setAppInactive(String packageName, boolean isInactive) { 381 mUsageStatsManager.setAppInactive(packageName, isInactive); 382 } 383 getActiveDataSubscriptionId()384 int getActiveDataSubscriptionId() { 385 return SubscriptionManager.getActiveDataSubscriptionId(); 386 } 387 } 388 } 389