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 
17 package com.android.internal.telephony;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.content.ServiceConnection;
26 import android.content.pm.ComponentInfo;
27 import android.content.pm.PackageManager;
28 import android.content.pm.ResolveInfo;
29 import android.os.Bundle;
30 import android.os.Handler;
31 import android.os.IBinder;
32 import android.os.Message;
33 import android.os.Process;
34 import android.os.SystemClock;
35 import android.os.UserHandle;
36 import android.service.carrier.CarrierService;
37 import android.telephony.PackageChangeReceiver;
38 import android.telephony.SubscriptionManager;
39 import android.telephony.TelephonyManager;
40 import android.text.TextUtils;
41 import android.util.Log;
42 import android.util.SparseArray;
43 
44 import com.android.internal.annotations.VisibleForTesting;
45 import com.android.internal.telephony.util.TelephonyUtils;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 import java.util.List;
50 
51 /**
52  * Manages long-lived bindings to carrier services
53  * @hide
54  */
55 public class CarrierServiceBindHelper {
56     private static final String LOG_TAG = "CarrierSvcBindHelper";
57 
58     /**
59      * How long to linger a binding after an app loses carrier privileges, as long as no new
60      * binding comes in to take its place.
61      */
62     private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds
63 
64     @UnsupportedAppUsage
65     private Context mContext;
66     @VisibleForTesting
67     public SparseArray<AppBinding> mBindings = new SparseArray();
68     @VisibleForTesting
69     public SparseArray<String> mLastSimState = new SparseArray<>();
70     private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor();
71 
72     // whether we have successfully bound to the service
73     private boolean mServiceBound = false;
74 
75     private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
76         @Override
77         public void onReceive(Context context, Intent intent) {
78             final String action = intent.getAction();
79             log("Received " + action);
80 
81             if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
82                 // On user unlock, new components might become available, so reevaluate all
83                 // bindings.
84                 for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) {
85                     mBindings.get(phoneId).rebind();
86                 }
87             }
88         }
89     };
90 
91     private static final int EVENT_REBIND = 0;
92     @VisibleForTesting
93     public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
94     @VisibleForTesting
95     public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2;
96 
97     @UnsupportedAppUsage
98     @VisibleForTesting
99     public Handler mHandler = new Handler() {
100         @Override
101         public void handleMessage(Message msg) {
102             int phoneId;
103             AppBinding binding;
104             log("mHandler: " + msg.what);
105 
106             switch (msg.what) {
107                 case EVENT_REBIND:
108                     phoneId = (int) msg.obj;
109                     binding = mBindings.get(phoneId);
110                     if (binding == null) return;
111                     log("Rebinding if necessary for phoneId: " + binding.getPhoneId());
112                     binding.rebind();
113                     break;
114                 case EVENT_PERFORM_IMMEDIATE_UNBIND:
115                     phoneId = (int) msg.obj;
116                     binding = mBindings.get(phoneId);
117                     if (binding == null) return;
118                     binding.performImmediateUnbind();
119                     break;
120                 case EVENT_MULTI_SIM_CONFIG_CHANGED:
121                     updateBindingsAndSimStates();
122                     break;
123             }
124         }
125     };
126 
CarrierServiceBindHelper(Context context)127     public CarrierServiceBindHelper(Context context) {
128         mContext = context;
129 
130         updateBindingsAndSimStates();
131 
132         PhoneConfigurationManager.registerForMultiSimConfigChange(
133                 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
134 
135         mPackageMonitor.register(
136                 context, mHandler.getLooper(), UserHandle.ALL);
137         try {
138             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
139                 0, UserHandle.SYSTEM);
140             contextAsUser.registerReceiver(mUserUnlockedReceiver,
141                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
142                 mHandler);
143         } catch (PackageManager.NameNotFoundException e) {
144             loge("Package name not found: " + e.getMessage());
145         }
146     }
147 
148     // Create or dispose mBindings and mLastSimState objects.
updateBindingsAndSimStates()149     private void updateBindingsAndSimStates() {
150         int prevLen = mBindings.size();
151         int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
152                 .getActiveModemCount();
153 
154         // If prevLen < newLen, allocate AppBinding and simState objects.
155         for (int phoneId = prevLen; phoneId < newLen; phoneId++) {
156             mBindings.put(phoneId, new AppBinding(phoneId));
157             mLastSimState.put(phoneId, new String());
158         }
159 
160         // If prevLen > newLen, dispose AppBinding and simState objects.
161         for (int phoneId = newLen; phoneId < prevLen; phoneId++) {
162             mBindings.get(phoneId).unbind(true);
163             mBindings.delete(phoneId);
164             mLastSimState.delete(phoneId);
165         }
166     }
167 
updateForPhoneId(int phoneId, String simState)168     void updateForPhoneId(int phoneId, String simState) {
169         log("update binding for phoneId: " + phoneId + " simState: " + simState);
170         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
171             return;
172         }
173         if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return;
174         if (simState.equals(mLastSimState.get(phoneId))) {
175             // ignore consecutive duplicated events
176             return;
177         } else {
178             mLastSimState.put(phoneId, simState);
179         }
180         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId));
181     }
182 
183     private class AppBinding {
184         private int phoneId;
185         private CarrierServiceConnection connection;
186         private int bindCount;
187         private long lastBindStartMillis;
188         private int unbindCount;
189         private long lastUnbindMillis;
190         private String carrierPackage;
191         private String carrierServiceClass;
192         private long mUnbindScheduledUptimeMillis = -1;
193 
AppBinding(int phoneId)194         public AppBinding(int phoneId) {
195             this.phoneId = phoneId;
196         }
197 
getPhoneId()198         public int getPhoneId() {
199             return phoneId;
200         }
201 
202         /** Return the package that is currently being bound to, or null if there is no binding. */
getPackage()203         public String getPackage() {
204             return carrierPackage;
205         }
206 
207         /**
208          * Update the bindings for the current carrier app for this phone.
209          *
210          * <p>Safe to call even if a binding already exists. If the current binding is invalid, it
211          * will be dropped. If it is valid, it will be left untouched.
212          */
rebind()213         void rebind() {
214             // Get the package name for the carrier app
215             List<String> carrierPackageNames =
216                 TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone(
217                     new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId
218                 );
219 
220             if (carrierPackageNames == null || carrierPackageNames.size() <= 0) {
221                 log("No carrier app for: " + phoneId);
222                 // Unbind after a delay in case this is a temporary blip in carrier privileges.
223                 unbind(false /* immediate */);
224                 return;
225             }
226 
227             log("Found carrier app: " + carrierPackageNames);
228             String candidateCarrierPackage = carrierPackageNames.get(0);
229             // If we are binding to a different package, unbind immediately from the current one.
230             if (!TextUtils.equals(carrierPackage, candidateCarrierPackage)) {
231                 unbind(true /* immediate */);
232             }
233 
234             // Look up the carrier service
235             Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
236             carrierService.setPackage(candidateCarrierPackage);
237 
238             ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
239                 carrierService, PackageManager.GET_META_DATA);
240             Bundle metadata = null;
241             String candidateServiceClass = null;
242             if (carrierResolveInfo != null) {
243                 metadata = carrierResolveInfo.serviceInfo.metaData;
244                 ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo);
245                 candidateServiceClass = new ComponentName(componentInfo.packageName,
246                     componentInfo.name).getClassName();
247             }
248 
249             // Only bind if the service wants it
250             if (metadata == null ||
251                 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
252                 log("Carrier app does not want a long lived binding");
253                 unbind(true /* immediate */);
254                 return;
255             }
256 
257             if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) {
258                 // Unbind immediately if the carrier service component has changed.
259                 unbind(true /* immediate */);
260             } else if (connection != null) {
261                 // Component is unchanged and connection is up - do nothing, but cancel any
262                 // scheduled unbinds.
263                 cancelScheduledUnbind();
264                 return;
265             }
266 
267             carrierPackage = candidateCarrierPackage;
268             carrierServiceClass = candidateServiceClass;
269 
270             log("Binding to " + carrierPackage + " for phone " + phoneId);
271 
272             // Log debug information
273             bindCount++;
274             lastBindStartMillis = System.currentTimeMillis();
275 
276             connection = new CarrierServiceConnection();
277 
278             String error;
279             try {
280                 if (mContext.createContextAsUser(Process.myUserHandle(), 0)
281                         .bindService(carrierService,
282                                 Context.BIND_AUTO_CREATE
283                                 | Context.BIND_FOREGROUND_SERVICE
284                                 | Context.BIND_INCLUDE_CAPABILITIES,
285                                 (r) -> mHandler.post(r),
286                                 connection)) {
287                     log("service bound");
288                     mServiceBound = true;
289                     return;
290                 }
291 
292                 error = "bindService returned false";
293             } catch (SecurityException ex) {
294                 error = ex.getMessage();
295             }
296 
297             log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
298                 ". Error: " + error);
299             unbind(true /* immediate */);
300         }
301 
302         /**
303          * Release the binding.
304          *
305          * @param immediate whether the binding should be released immediately or after a short
306          *                  delay. This should be true unless the reason for the unbind is that no
307          *                  app has carrier privileges, in which case it is useful to delay
308          *                  unbinding in case this is a temporary SIM blip.
309          */
unbind(boolean immediate)310         void unbind(boolean immediate) {
311             if (connection == null) {
312                 // Already fully unbound.
313                 return;
314             }
315 
316             // Only let the binding linger if a delayed unbind is requested *and* the connection is
317             // currently active. If the connection is down, unbind immediately as the app is likely
318             // not running anyway and it may be a permanent disconnection (e.g. the app was
319             // disabled).
320             if (immediate || !connection.connected) {
321                 cancelScheduledUnbind();
322                 performImmediateUnbind();
323             } else if (mUnbindScheduledUptimeMillis == -1) {
324                 long currentUptimeMillis = SystemClock.uptimeMillis();
325                 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
326                 log("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
327                 mHandler.sendMessageAtTime(
328                         mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId),
329                         mUnbindScheduledUptimeMillis);
330             }
331         }
332 
performImmediateUnbind()333         private void performImmediateUnbind() {
334             // Log debug information
335             unbindCount++;
336             lastUnbindMillis = System.currentTimeMillis();
337 
338             // Clear package state now that no binding is desired.
339             carrierPackage = null;
340             carrierServiceClass = null;
341 
342             // Actually unbind
343             if (mServiceBound) {
344                 log("Unbinding from carrier app");
345                 mServiceBound = false;
346                 try {
347                     mContext.unbindService(connection);
348                 } catch (IllegalArgumentException e) {
349                     //TODO(b/151328766): Figure out why we unbind without binding
350                     loge("Tried to unbind without binding e=" + e);
351                 }
352             } else {
353                 log("Not bound, skipping unbindService call");
354             }
355             connection = null;
356             mUnbindScheduledUptimeMillis = -1;
357         }
358 
cancelScheduledUnbind()359         private void cancelScheduledUnbind() {
360             mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND);
361             mUnbindScheduledUptimeMillis = -1;
362         }
363 
dump(FileDescriptor fd, PrintWriter pw, String[] args)364         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
365             pw.println("Carrier app binding for phone " + phoneId);
366             pw.println("  connection: " + connection);
367             pw.println("  bindCount: " + bindCount);
368             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
369             pw.println("  unbindCount: " + unbindCount);
370             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
371             pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
372             pw.println();
373         }
374     }
375 
376     private class CarrierServiceConnection implements ServiceConnection {
377         private boolean connected;
378 
379         @Override
onServiceConnected(ComponentName name, IBinder service)380         public void onServiceConnected(ComponentName name, IBinder service) {
381             log("Connected to carrier app: " + name.flattenToString());
382             connected = true;
383         }
384 
385         @Override
onServiceDisconnected(ComponentName name)386         public void onServiceDisconnected(ComponentName name) {
387             log("Disconnected from carrier app: " + name.flattenToString());
388             connected = false;
389         }
390 
391         @Override
onBindingDied(ComponentName name)392         public void onBindingDied(ComponentName name) {
393             log("Binding from carrier app died: " + name.flattenToString());
394             connected = false;
395         }
396 
397         @Override
onNullBinding(ComponentName name)398         public void onNullBinding(ComponentName name) {
399             log("Null binding from carrier app: " + name.flattenToString());
400             connected = false;
401         }
402 
403         @Override
toString()404         public String toString() {
405             return "CarrierServiceConnection[connected=" + connected + "]";
406         }
407     }
408 
409     private class CarrierServicePackageMonitor extends PackageChangeReceiver {
410         @Override
onPackageAdded(String packageName)411         public void onPackageAdded(String packageName) {
412             evaluateBinding(packageName, true /* forceUnbind */);
413         }
414 
415         @Override
onPackageRemoved(String packageName)416         public void onPackageRemoved(String packageName) {
417             evaluateBinding(packageName, true /* forceUnbind */);
418         }
419 
420         @Override
onPackageUpdateFinished(String packageName)421         public void onPackageUpdateFinished(String packageName) {
422             evaluateBinding(packageName, true /* forceUnbind */);
423         }
424 
425         @Override
onPackageModified(String packageName)426         public void onPackageModified(String packageName) {
427             evaluateBinding(packageName, false /* forceUnbind */);
428         }
429 
430         @Override
onHandleForceStop(String[] packages, boolean doit)431         public void onHandleForceStop(String[] packages, boolean doit) {
432             if (doit) {
433                 for (String packageName : packages) {
434                     evaluateBinding(packageName, true /* forceUnbind */);
435                 }
436             }
437         }
438 
evaluateBinding(String carrierPackageName, boolean forceUnbind)439         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
440             for (int i = 0; i < mBindings.size(); i++) {
441                 AppBinding appBinding = mBindings.get(i);
442                 String appBindingPackage = appBinding.getPackage();
443                 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
444                 // Only log if this package was a carrier package to avoid log spam in the common
445                 // case that there are no carrier packages, but evaluate the binding if the package
446                 // is unset, in case this package change resulted in a new carrier package becoming
447                 // available for binding.
448                 if (isBindingForPackage) {
449                     log(carrierPackageName + " changed and corresponds to a phone. Rebinding.");
450                 }
451                 if (appBindingPackage == null || isBindingForPackage) {
452                     if (forceUnbind) {
453                         appBinding.unbind(true /* immediate */);
454                     }
455                     appBinding.rebind();
456                 }
457             }
458         }
459     }
460 
log(String message)461     private static void log(String message) {
462         Log.d(LOG_TAG, message);
463     }
464 
loge(String message)465     private static void loge(String message) { Log.e(LOG_TAG, message); }
466 
dump(FileDescriptor fd, PrintWriter pw, String[] args)467     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
468         pw.println("CarrierServiceBindHelper:");
469         for (int i = 0; i < mBindings.size(); i++) {
470             mBindings.get(i).dump(fd, pw, args);
471         }
472     }
473 }
474