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