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