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