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 | Context.BIND_FOREGROUND_SERVICE,
283                                 (r) -> mHandler.post(r),
284                                 connection)) {
285                     log("service bound");
286                     mServiceBound = true;
287                     return;
288                 }
289 
290                 error = "bindService returned false";
291             } catch (SecurityException ex) {
292                 error = ex.getMessage();
293             }
294 
295             log("Unable to bind to " + carrierPackage + " for phone " + phoneId +
296                 ". Error: " + error);
297             unbind(true /* immediate */);
298         }
299 
300         /**
301          * Release the binding.
302          *
303          * @param immediate whether the binding should be released immediately or after a short
304          *                  delay. This should be true unless the reason for the unbind is that no
305          *                  app has carrier privileges, in which case it is useful to delay
306          *                  unbinding in case this is a temporary SIM blip.
307          */
unbind(boolean immediate)308         void unbind(boolean immediate) {
309             if (connection == null) {
310                 // Already fully unbound.
311                 return;
312             }
313 
314             // Only let the binding linger if a delayed unbind is requested *and* the connection is
315             // currently active. If the connection is down, unbind immediately as the app is likely
316             // not running anyway and it may be a permanent disconnection (e.g. the app was
317             // disabled).
318             if (immediate || !connection.connected) {
319                 cancelScheduledUnbind();
320                 performImmediateUnbind();
321             } else if (mUnbindScheduledUptimeMillis == -1) {
322                 long currentUptimeMillis = SystemClock.uptimeMillis();
323                 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
324                 log("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
325                 mHandler.sendMessageAtTime(
326                         mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId),
327                         mUnbindScheduledUptimeMillis);
328             }
329         }
330 
performImmediateUnbind()331         private void performImmediateUnbind() {
332             // Log debug information
333             unbindCount++;
334             lastUnbindMillis = System.currentTimeMillis();
335 
336             // Clear package state now that no binding is desired.
337             carrierPackage = null;
338             carrierServiceClass = null;
339 
340             // Actually unbind
341             if (mServiceBound) {
342                 log("Unbinding from carrier app");
343                 mServiceBound = false;
344                 try {
345                     mContext.unbindService(connection);
346                 } catch (IllegalArgumentException e) {
347                     //TODO(b/151328766): Figure out why we unbind without binding
348                     loge("Tried to unbind without binding e=" + e);
349                 }
350             } else {
351                 log("Not bound, skipping unbindService call");
352             }
353             connection = null;
354             mUnbindScheduledUptimeMillis = -1;
355         }
356 
cancelScheduledUnbind()357         private void cancelScheduledUnbind() {
358             mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND);
359             mUnbindScheduledUptimeMillis = -1;
360         }
361 
dump(FileDescriptor fd, PrintWriter pw, String[] args)362         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
363             pw.println("Carrier app binding for phone " + phoneId);
364             pw.println("  connection: " + connection);
365             pw.println("  bindCount: " + bindCount);
366             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
367             pw.println("  unbindCount: " + unbindCount);
368             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
369             pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
370             pw.println();
371         }
372     }
373 
374     private class CarrierServiceConnection implements ServiceConnection {
375         private boolean connected;
376 
377         @Override
onServiceConnected(ComponentName name, IBinder service)378         public void onServiceConnected(ComponentName name, IBinder service) {
379             log("Connected to carrier app: " + name.flattenToString());
380             connected = true;
381         }
382 
383         @Override
onServiceDisconnected(ComponentName name)384         public void onServiceDisconnected(ComponentName name) {
385             log("Disconnected from carrier app: " + name.flattenToString());
386             connected = false;
387         }
388 
389         @Override
onBindingDied(ComponentName name)390         public void onBindingDied(ComponentName name) {
391             log("Binding from carrier app died: " + name.flattenToString());
392             connected = false;
393         }
394 
395         @Override
onNullBinding(ComponentName name)396         public void onNullBinding(ComponentName name) {
397             log("Null binding from carrier app: " + name.flattenToString());
398             connected = false;
399         }
400 
401         @Override
toString()402         public String toString() {
403             return "CarrierServiceConnection[connected=" + connected + "]";
404         }
405     }
406 
407     private class CarrierServicePackageMonitor extends PackageChangeReceiver {
408         @Override
onPackageAdded(String packageName)409         public void onPackageAdded(String packageName) {
410             evaluateBinding(packageName, true /* forceUnbind */);
411         }
412 
413         @Override
onPackageRemoved(String packageName)414         public void onPackageRemoved(String packageName) {
415             evaluateBinding(packageName, true /* forceUnbind */);
416         }
417 
418         @Override
onPackageUpdateFinished(String packageName)419         public void onPackageUpdateFinished(String packageName) {
420             evaluateBinding(packageName, true /* forceUnbind */);
421         }
422 
423         @Override
onPackageModified(String packageName)424         public void onPackageModified(String packageName) {
425             evaluateBinding(packageName, false /* forceUnbind */);
426         }
427 
428         @Override
onHandleForceStop(String[] packages, boolean doit)429         public void onHandleForceStop(String[] packages, boolean doit) {
430             if (doit) {
431                 for (String packageName : packages) {
432                     evaluateBinding(packageName, true /* forceUnbind */);
433                 }
434             }
435         }
436 
evaluateBinding(String carrierPackageName, boolean forceUnbind)437         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
438             for (int i = 0; i < mBindings.size(); i++) {
439                 AppBinding appBinding = mBindings.get(i);
440                 String appBindingPackage = appBinding.getPackage();
441                 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
442                 // Only log if this package was a carrier package to avoid log spam in the common
443                 // case that there are no carrier packages, but evaluate the binding if the package
444                 // is unset, in case this package change resulted in a new carrier package becoming
445                 // available for binding.
446                 if (isBindingForPackage) {
447                     log(carrierPackageName + " changed and corresponds to a phone. Rebinding.");
448                 }
449                 if (appBindingPackage == null || isBindingForPackage) {
450                     if (forceUnbind) {
451                         appBinding.unbind(true /* immediate */);
452                     }
453                     appBinding.rebind();
454                 }
455             }
456         }
457     }
458 
log(String message)459     private static void log(String message) {
460         Log.d(LOG_TAG, message);
461     }
462 
loge(String message)463     private static void loge(String message) { Log.e(LOG_TAG, message); }
464 
dump(FileDescriptor fd, PrintWriter pw, String[] args)465     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
466         pw.println("CarrierServiceBindHelper:");
467         for (int i = 0; i < mBindings.size(); i++) {
468             mBindings.get(i).dump(fd, pw, args);
469         }
470     }
471 }
472