• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.NonNull;
20 import android.app.ActivityManager;
21 import android.compat.annotation.UnsupportedAppUsage;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.IntentFilter;
27 import android.content.ServiceConnection;
28 import android.content.pm.ComponentInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.os.Build;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.HandlerExecutor;
35 import android.os.IBinder;
36 import android.os.Message;
37 import android.os.Process;
38 import android.os.SystemClock;
39 import android.os.UserHandle;
40 import android.service.carrier.CarrierService;
41 import android.telephony.SubscriptionManager;
42 import android.telephony.TelephonyManager;
43 import android.telephony.TelephonyRegistryManager;
44 import android.text.TextUtils;
45 import android.util.LocalLog;
46 import android.util.Log;
47 import android.util.SparseArray;
48 
49 import com.android.internal.annotations.VisibleForTesting;
50 import com.android.internal.telephony.flags.Flags;
51 import com.android.internal.telephony.util.TelephonyUtils;
52 
53 import java.io.FileDescriptor;
54 import java.io.PrintWriter;
55 import java.util.Arrays;
56 import java.util.Set;
57 
58 /**
59  * Manages long-lived bindings to carrier services
60  * @hide
61  */
62 public class CarrierServiceBindHelper {
63     private static final String LOG_TAG = "CarrierSvcBindHelper";
64 
65     // TODO(b/201423849): Remove the UNBIND_DELAY_MILLIS and switch to CarrierPrivilegesCallback
66     // The grace period has been replaced by CarrierPrivilegesTracker. CarrierPrivilegesCallback has
67     // provided the callback for both carrier privileges change and carrier service change (with
68     // awareness of the grace period), the delay based logic here should be cleaned up.
69     /**
70      * How long to linger a binding after an app loses carrier privileges, as long as no new
71      * binding comes in to take its place.
72      */
73     private static final int UNBIND_DELAY_MILLIS = 30 * 1000; // 30 seconds
74 
75     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
76     private Context mContext;
77     @VisibleForTesting
78     public SparseArray<AppBinding> mBindings = new SparseArray();
79     @VisibleForTesting
80     public SparseArray<String> mLastSimState = new SparseArray<>();
81     // TODO(b/201423849): Clean up PackageChangeReceiver/UserUnlockedReceiver/SIM State change if
82     // CarrierServiceChangeCallback can cover the cases
83     private final PackageChangeReceiver mPackageMonitor = new CarrierServicePackageMonitor();
84     private final LocalLog mLocalLog = new LocalLog(100);
85 
86     private BroadcastReceiver mUserUnlockedReceiver = new BroadcastReceiver() {
87         @Override
88         public void onReceive(Context context, Intent intent) {
89             final String action = intent.getAction();
90             logdWithLocalLog("Received " + action);
91 
92             if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
93                 // On user unlock, new components might become available, so reevaluate all
94                 // bindings.
95                 for (int phoneId = 0; phoneId < mBindings.size(); phoneId++) {
96                     mBindings.get(phoneId).rebind();
97                 }
98             }
99         }
100     };
101 
102     private class CarrierServiceChangeCallback implements
103             TelephonyManager.CarrierPrivilegesCallback {
104         final int mPhoneId;
105 
CarrierServiceChangeCallback(int phoneId)106         CarrierServiceChangeCallback(int phoneId) {
107             this.mPhoneId = phoneId;
108         }
109 
110         @Override
onCarrierPrivilegesChanged(Set<String> privilegedPackageNames, Set<Integer> privilegedUids)111         public void onCarrierPrivilegesChanged(Set<String> privilegedPackageNames,
112                 Set<Integer> privilegedUids) {
113             // Ignored, not interested here
114         }
115 
116         @Override
onCarrierServiceChanged(String carrierServicePackageName, int carrierServiceUid)117         public void onCarrierServiceChanged(String carrierServicePackageName,
118                 int carrierServiceUid) {
119             logdWithLocalLog("onCarrierServiceChanged, carrierServicePackageName="
120                     + carrierServicePackageName + ", carrierServiceUid=" + carrierServiceUid
121                     + ", mPhoneId=" + mPhoneId);
122             mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, mPhoneId));
123         }
124     }
125 
126     private static final int EVENT_REBIND = 0;
127     @VisibleForTesting
128     public static final int EVENT_PERFORM_IMMEDIATE_UNBIND = 1;
129     @VisibleForTesting
130     public static final int EVENT_MULTI_SIM_CONFIG_CHANGED = 2;
131 
132     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
133     @VisibleForTesting
134     public Handler mHandler = new Handler() {
135         @Override
136         public void handleMessage(Message msg) {
137             int phoneId;
138             AppBinding binding;
139             logdWithLocalLog("mHandler: " + msg.what);
140 
141             switch (msg.what) {
142                 case EVENT_REBIND:
143                     phoneId = (int) msg.obj;
144                     binding = mBindings.get(phoneId);
145                     if (binding == null) return;
146                     logdWithLocalLog("Rebinding if necessary for phoneId: " + binding.getPhoneId());
147                     binding.rebind();
148                     break;
149                 case EVENT_PERFORM_IMMEDIATE_UNBIND:
150                     phoneId = (int) msg.obj;
151                     binding = mBindings.get(phoneId);
152                     if (binding == null) return;
153                     logdWithLocalLog("Unbind immediate with phoneId: " + binding.getPhoneId());
154                     binding.performImmediateUnbind();
155                     break;
156                 case EVENT_MULTI_SIM_CONFIG_CHANGED:
157                     updateBindingsAndSimStates();
158                     break;
159                 default:
160                     Log.e(LOG_TAG, "Unsupported event received: " + msg.what);
161             }
162         }
163     };
164 
CarrierServiceBindHelper(Context context)165     public CarrierServiceBindHelper(Context context) {
166         mContext =
167                 context.createContextAsUser(
168                         Flags.supportCarrierServicesForHsum()
169                         ? UserHandle.of(ActivityManager.getCurrentUser())
170                         : Process.myUserHandle(), 0);
171 
172         updateBindingsAndSimStates();
173 
174         PhoneConfigurationManager.registerForMultiSimConfigChange(
175                 mHandler, EVENT_MULTI_SIM_CONFIG_CHANGED, null);
176 
177         mPackageMonitor.register(
178                 context, mHandler.getLooper(), UserHandle.ALL);
179         try {
180             Context contextAsUser = mContext.createPackageContextAsUser(mContext.getPackageName(),
181                 0, Flags.supportCarrierServicesForHsum() ? UserHandle.CURRENT : UserHandle.SYSTEM);
182             contextAsUser.registerReceiver(mUserUnlockedReceiver,
183                 new IntentFilter(Intent.ACTION_USER_UNLOCKED), null /* broadcastPermission */,
184                 mHandler);
185         } catch (PackageManager.NameNotFoundException e) {
186             logeWithLocalLog("Package name not found: " + e.getMessage());
187         }
188     }
189 
190     // Create or dispose mBindings and mLastSimState objects.
updateBindingsAndSimStates()191     private void updateBindingsAndSimStates() {
192         int prevLen = mBindings.size();
193         int newLen = ((TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE))
194                 .getActiveModemCount();
195 
196         // If prevLen < newLen, allocate AppBinding and simState objects.
197         for (int phoneId = prevLen; phoneId < newLen; phoneId++) {
198             mBindings.put(phoneId, new AppBinding(phoneId));
199             mLastSimState.put(phoneId, new String());
200         }
201 
202         // If prevLen > newLen, dispose AppBinding and simState objects.
203         for (int phoneId = newLen; phoneId < prevLen; phoneId++) {
204             mBindings.get(phoneId).tearDown();
205             mBindings.get(phoneId).unbind(true);
206             mBindings.delete(phoneId);
207             mLastSimState.delete(phoneId);
208         }
209     }
210 
211     /**
212      * Update SIM state.
213      *
214      * @param phoneId The phone id.
215      * @param simState The legacy SIM state.
216      */
updateForPhoneId(int phoneId, @NonNull String simState)217     public void updateForPhoneId(int phoneId, @NonNull String simState) {
218         logdWithLocalLog("update binding for phoneId: " + phoneId + " simState: " + simState);
219         if (!SubscriptionManager.isValidPhoneId(phoneId)) {
220             return;
221         }
222         if (TextUtils.isEmpty(simState) || phoneId >= mLastSimState.size()) return;
223         if (simState.equals(mLastSimState.get(phoneId))) {
224             // ignore consecutive duplicated events
225             return;
226         } else {
227             mLastSimState.put(phoneId, simState);
228         }
229         mHandler.sendMessage(mHandler.obtainMessage(EVENT_REBIND, phoneId));
230     }
231 
232     private class AppBinding {
233         private int phoneId;
234         private CarrierServiceConnection connection;
235         private int bindCount;
236         private long lastBindStartMillis;
237         private int unbindCount;
238         private long lastUnbindMillis;
239         private String carrierPackage;
240         private String carrierServiceClass;
241         private long mUnbindScheduledUptimeMillis = -1;
242         private final CarrierServiceChangeCallback mCarrierServiceChangeCallback;
243 
AppBinding(int phoneId)244         public AppBinding(int phoneId) {
245             this.phoneId = phoneId;
246             this.mCarrierServiceChangeCallback = new CarrierServiceChangeCallback(phoneId);
247             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
248             if (tm != null) {
249                 tm.registerCarrierPrivilegesCallback(phoneId, new HandlerExecutor(mHandler),
250                         mCarrierServiceChangeCallback);
251             }
252         }
253 
tearDown()254         public void tearDown() {
255             TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
256             if (tm != null && mCarrierServiceChangeCallback != null) {
257                 tm.unregisterCarrierPrivilegesCallback(mCarrierServiceChangeCallback);
258             }
259         }
260 
getPhoneId()261         public int getPhoneId() {
262             return phoneId;
263         }
264 
265         /** Return the package that is currently being bound to, or null if there is no binding. */
getPackage()266         public String getPackage() {
267             return carrierPackage;
268         }
269 
270         /**
271          * Update the bindings for the current carrier app for this phone.
272          *
273          * <p>Safe to call even if a binding already exists. If the current binding is invalid, it
274          * will be dropped. If it is valid, it will be left untouched.
275          */
rebind()276         void rebind() {
277             // Get the package name for the carrier app
278             String carrierPackageName = TelephonyManager.from(
279                     mContext).getCarrierServicePackageNameForLogicalSlot(phoneId);
280 
281             if (carrierPackageName == null) {
282                 logdWithLocalLog("No carrier app for: " + phoneId);
283                 // Unbind after a delay in case this is a temporary blip in carrier privileges.
284                 unbind(false /* immediate */);
285                 return;
286             }
287 
288             logdWithLocalLog("Found carrier app: " + carrierPackageName);
289             // If we are binding to a different package, unbind immediately from the current one.
290             if (!TextUtils.equals(carrierPackage, carrierPackageName)) {
291                 unbind(true /* immediate */);
292             }
293 
294             // Look up the carrier service
295             Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE);
296             carrierService.setPackage(carrierPackageName);
297 
298             ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService(
299                 carrierService, PackageManager.GET_META_DATA);
300             Bundle metadata = null;
301             String candidateServiceClass = null;
302             if (carrierResolveInfo != null) {
303                 metadata = carrierResolveInfo.serviceInfo.metaData;
304                 ComponentInfo componentInfo = TelephonyUtils.getComponentInfo(carrierResolveInfo);
305                 candidateServiceClass = new ComponentName(componentInfo.packageName,
306                     componentInfo.name).getClassName();
307             }
308 
309             // Only bind if the service wants it
310             if (metadata == null ||
311                 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) {
312                 logdWithLocalLog("Carrier app does not want a long lived binding");
313                 unbind(true /* immediate */);
314                 return;
315             }
316 
317             if (!TextUtils.equals(carrierServiceClass, candidateServiceClass)) {
318                 logdWithLocalLog("CarrierService class changed, unbind immediately.");
319                 // Unbind immediately if the carrier service component has changed.
320                 unbind(true /* immediate */);
321             } else if (connection != null) {
322                 logdWithLocalLog(
323                         "CarrierService class unchanged with connection up, cancelScheduledUnbind");
324                 // Component is unchanged and connection is up - do nothing, but cancel any
325                 // scheduled unbinds.
326                 cancelScheduledUnbind();
327                 return;
328             }
329 
330             carrierPackage = carrierPackageName;
331             carrierServiceClass = candidateServiceClass;
332 
333             logdWithLocalLog("Binding to " + carrierPackage + " for phone " + phoneId);
334 
335             // Log debug information
336             bindCount++;
337             lastBindStartMillis = System.currentTimeMillis();
338 
339             connection = new CarrierServiceConnection(getPhoneId());
340 
341             String error;
342             try {
343                 if (mContext.bindService(
344                         carrierService,
345                         Context.BIND_AUTO_CREATE
346                                 | Context.BIND_FOREGROUND_SERVICE
347                                 | Context.BIND_INCLUDE_CAPABILITIES,
348                         (r) -> mHandler.post(r),
349                         connection)) {
350                     logdWithLocalLog("service bound");
351                     return;
352                 }
353 
354                 error = "bindService returned false";
355             } catch (SecurityException ex) {
356                 error = ex.getMessage();
357             }
358 
359             logdWithLocalLog("Unable to bind to " + carrierPackage + " for phone " + phoneId
360                     + ". Error: " + error);
361             unbind(true /* immediate */);
362         }
363 
364         /**
365          * Release the binding.
366          *
367          * @param immediate whether the binding should be released immediately or after a short
368          *                  delay. This should be true unless the reason for the unbind is that no
369          *                  app has carrier privileges, in which case it is useful to delay
370          *                  unbinding in case this is a temporary SIM blip.
371          */
unbind(boolean immediate)372         void unbind(boolean immediate) {
373             if (connection == null) {
374                 // Already fully unbound.
375                 return;
376             }
377 
378             // Only let the binding linger if a delayed unbind is requested *and* the connection is
379             // currently active. If the connection is down, unbind immediately as the app is likely
380             // not running anyway and it may be a permanent disconnection (e.g. the app was
381             // disabled).
382             if (immediate || !connection.connected) {
383                 logdWithLocalLog("unbind immediately or with disconnected connection");
384                 cancelScheduledUnbind();
385                 performImmediateUnbind();
386             } else if (mUnbindScheduledUptimeMillis == -1) {
387                 long currentUptimeMillis = SystemClock.uptimeMillis();
388                 mUnbindScheduledUptimeMillis = currentUptimeMillis + UNBIND_DELAY_MILLIS;
389                 logdWithLocalLog("Scheduling unbind in " + UNBIND_DELAY_MILLIS + " millis");
390                 mHandler.sendMessageAtTime(
391                         mHandler.obtainMessage(EVENT_PERFORM_IMMEDIATE_UNBIND, phoneId),
392                         mUnbindScheduledUptimeMillis);
393             }
394         }
395 
performImmediateUnbind()396         private void performImmediateUnbind() {
397             // Log debug information
398             unbindCount++;
399             lastUnbindMillis = System.currentTimeMillis();
400 
401             // Clear package state now that no binding is desired.
402             carrierPackage = null;
403             carrierServiceClass = null;
404 
405             // Always call unbindService, no matter if bindService succeed.
406             if (connection != null) {
407                 mContext.unbindService(connection);
408                 logdWithLocalLog("Unbinding from carrier app");
409                 connection = null;
410                 mUnbindScheduledUptimeMillis = -1;
411             }
412         }
413 
cancelScheduledUnbind()414         private void cancelScheduledUnbind() {
415             logdWithLocalLog("cancelScheduledUnbind");
416             mHandler.removeMessages(EVENT_PERFORM_IMMEDIATE_UNBIND);
417             mUnbindScheduledUptimeMillis = -1;
418         }
419 
dump(FileDescriptor fd, PrintWriter pw, String[] args)420         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
421             pw.println("Carrier app binding for phone " + phoneId);
422             pw.println("  connection: " + connection);
423             pw.println("  bindCount: " + bindCount);
424             pw.println("  lastBindStartMillis: " + lastBindStartMillis);
425             pw.println("  unbindCount: " + unbindCount);
426             pw.println("  lastUnbindMillis: " + lastUnbindMillis);
427             pw.println("  mUnbindScheduledUptimeMillis: " + mUnbindScheduledUptimeMillis);
428             pw.println("  mCarrierServiceChangeCallback: " + mCarrierServiceChangeCallback);
429             pw.println();
430         }
431     }
432 
433     private class CarrierServiceConnection implements ServiceConnection {
434         private boolean connected;
435         private final int mPhoneId;
436 
CarrierServiceConnection(int phoneId)437         CarrierServiceConnection(int phoneId) {
438             mPhoneId = phoneId;
439         }
440 
441         @Override
onServiceConnected(ComponentName name, IBinder service)442         public void onServiceConnected(ComponentName name, IBinder service) {
443             logdWithLocalLog("Connected to carrier app: " + name.flattenToString());
444             connected = true;
445         }
446 
maybeDisableCarrierNetworkChangeNotification()447         private void maybeDisableCarrierNetworkChangeNotification() {
448             int subscriptionId = SubscriptionManager.getSubscriptionId(mPhoneId);
449             // TODO(b/117525047): switch to phoneId-based solution when available in
450             // TelephonyRegistryManager to address SIM remove/disable case.
451             if (subscriptionId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
452                 logdWithLocalLog(
453                         "No valid subscription found when trying to disable carrierNetworkChange"
454                                 + " for phoneId: "
455                                 + mPhoneId);
456                 return;
457             }
458             TelephonyRegistryManager telephonyRegistryManager =
459                     mContext.getSystemService(TelephonyRegistryManager.class);
460             telephonyRegistryManager.notifyCarrierNetworkChange(subscriptionId, false);
461         }
462 
463         @Override
onServiceDisconnected(ComponentName name)464         public void onServiceDisconnected(ComponentName name) {
465             logdWithLocalLog("Disconnected from carrier app: " + name.flattenToString());
466             connected = false;
467             if (Flags.disableCarrierNetworkChangeOnCarrierAppLost()) {
468                 maybeDisableCarrierNetworkChangeNotification();
469             }
470         }
471 
472         @Override
onBindingDied(ComponentName name)473         public void onBindingDied(ComponentName name) {
474             logdWithLocalLog("Binding from carrier app died: " + name.flattenToString());
475             connected = false;
476             if (Flags.disableCarrierNetworkChangeOnCarrierAppLost()) {
477                 maybeDisableCarrierNetworkChangeNotification();
478             }
479         }
480 
481         @Override
onNullBinding(ComponentName name)482         public void onNullBinding(ComponentName name) {
483             logdWithLocalLog("Null binding from carrier app: " + name.flattenToString());
484             connected = false;
485         }
486 
487         @Override
toString()488         public String toString() {
489             return "CarrierServiceConnection[connected=" + connected + "]";
490         }
491     }
492 
493     private class CarrierServicePackageMonitor extends PackageChangeReceiver {
494         @Override
onPackageAdded(String packageName)495         public void onPackageAdded(String packageName) {
496             logdWithLocalLog("onPackageAdded: " + packageName);
497             evaluateBinding(packageName, true /* forceUnbind */);
498         }
499 
500         @Override
onPackageRemoved(String packageName)501         public void onPackageRemoved(String packageName) {
502             logdWithLocalLog("onPackageRemoved: " + packageName);
503             evaluateBinding(packageName, true /* forceUnbind */);
504         }
505 
506         @Override
onPackageUpdateFinished(String packageName)507         public void onPackageUpdateFinished(String packageName) {
508             logdWithLocalLog("onPackageUpdateFinished: " + packageName);
509             evaluateBinding(packageName, true /* forceUnbind */);
510         }
511 
512         @Override
onPackageModified(String packageName)513         public void onPackageModified(String packageName) {
514             logdWithLocalLog("onPackageModified: " + packageName);
515             evaluateBinding(packageName, false /* forceUnbind */);
516         }
517 
518         @Override
onHandleForceStop(String[] packages, boolean doit)519         public void onHandleForceStop(String[] packages, boolean doit) {
520             if (doit) {
521                 logdWithLocalLog("onHandleForceStop: " + Arrays.toString(packages));
522                 for (String packageName : packages) {
523                     evaluateBinding(packageName, true /* forceUnbind */);
524                 }
525             }
526         }
527 
evaluateBinding(String carrierPackageName, boolean forceUnbind)528         private void evaluateBinding(String carrierPackageName, boolean forceUnbind) {
529             for (int i = 0; i < mBindings.size(); i++) {
530                 AppBinding appBinding = mBindings.get(i);
531                 String appBindingPackage = appBinding.getPackage();
532                 boolean isBindingForPackage = carrierPackageName.equals(appBindingPackage);
533                 // Only log if this package was a carrier package to avoid log spam in the common
534                 // case that there are no carrier packages, but evaluate the binding if the package
535                 // is unset, in case this package change resulted in a new carrier package becoming
536                 // available for binding.
537                 if (isBindingForPackage) {
538                     logdWithLocalLog(
539                             carrierPackageName + " changed and corresponds to a phone. Rebinding.");
540                 }
541                 if (appBindingPackage == null || isBindingForPackage) {
542                     if (forceUnbind) {
543                         appBinding.unbind(true /* immediate */);
544                     }
545                     appBinding.rebind();
546                 }
547             }
548         }
549     }
550 
logdWithLocalLog(String msg)551     private void logdWithLocalLog(String msg) {
552         Log.d(LOG_TAG, msg);
553         mLocalLog.log(msg);
554     }
555 
logeWithLocalLog(String msg)556     private void logeWithLocalLog(String msg) {
557         Log.e(LOG_TAG, msg);
558         mLocalLog.log(msg);
559     }
560 
dump(FileDescriptor fd, PrintWriter pw, String[] args)561     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
562         pw.println("CarrierServiceBindHelper:");
563         for (int i = 0; i < mBindings.size(); i++) {
564             mBindings.get(i).dump(fd, pw, args);
565         }
566         pw.println("CarrierServiceBindHelperLogs=");
567         mLocalLog.dump(fd, pw, args);
568     }
569 }
570