CarrierServiceBindHelper.java revision 2dc7607bae115d015083afad7dec3a3beee7c4c3
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 17package com.android.internal.telephony; 18 19import android.content.BroadcastReceiver; 20import android.content.ComponentName; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.content.pm.PackageManager; 25import android.content.pm.ResolveInfo; 26import android.content.ServiceConnection; 27import android.os.Bundle; 28import android.os.Handler; 29import android.os.IBinder; 30import android.os.Message; 31import android.os.UserHandle; 32import android.telephony.SubscriptionManager; 33import android.telephony.TelephonyManager; 34import android.util.Log; 35import android.service.carrier.CarrierService; 36 37import com.android.internal.telephony.IccCardConstants; 38 39import java.io.FileDescriptor; 40import java.io.PrintWriter; 41import java.util.List; 42 43/** 44 * Manages long-lived bindings to carrier services 45 * @hide 46 */ 47public class CarrierServiceBindHelper { 48 private static final String LOG_TAG = CarrierServiceBindHelper.class.getSimpleName(); 49 50 private Context mContext; 51 private AppBinding[] mBindings; 52 private final BroadcastReceiver mReceiver = new PackageChangedBroadcastReceiver(); 53 54 private static final int EVENT_BIND = 0; 55 private static final int EVENT_UNBIND = 1; 56 private static final int EVENT_BIND_TIMEOUT = 2; 57 private static final int EVENT_PACKAGE_CHANGED = 3; 58 59 private static final int BIND_TIMEOUT_MILLIS = 10000; 60 61 private Handler mHandler = new Handler() { 62 @Override 63 public void handleMessage(Message msg) { 64 String carrierPackageName; 65 AppBinding binding; 66 log("mHandler: " + msg.what); 67 68 CarrierServiceConnection connection; 69 switch (msg.what) { 70 case EVENT_BIND: 71 binding = (AppBinding) msg.obj; 72 log("Binding to phoneId: " + binding.getPhoneId()); 73 binding.bind(); 74 break; 75 case EVENT_BIND_TIMEOUT: 76 binding = (AppBinding) msg.obj; 77 log("Bind timeout for phoneId: " + binding.getPhoneId()); 78 binding.unbind(); 79 break; 80 case EVENT_UNBIND: 81 binding = (AppBinding) msg.obj; 82 log("Unbinding for phoneId: " + binding.getPhoneId()); 83 binding.unbind(); 84 break; 85 case EVENT_PACKAGE_CHANGED: 86 carrierPackageName = (String) msg.obj; 87 for (AppBinding appBinding : mBindings) { 88 if (carrierPackageName.equals(appBinding.getPackage())) { 89 log(carrierPackageName + " changed and corresponds to a phone. Rebinding."); 90 appBinding.bind(); 91 } 92 } 93 break; 94 } 95 } 96 }; 97 98 public CarrierServiceBindHelper(Context context) { 99 mContext = context; 100 101 int numPhones = TelephonyManager.from(context).getPhoneCount(); 102 mBindings = new AppBinding[numPhones]; 103 104 for (int phoneId = 0; phoneId < numPhones; phoneId++) { 105 mBindings[phoneId] = new AppBinding(phoneId); 106 } 107 108 // Register for package updates. Update app or uninstall app update will have all 3 intents, 109 // in the order or removed, added, replaced, all with extra_replace set to true. 110 IntentFilter pkgFilter = new IntentFilter(); 111 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED); 112 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 113 pkgFilter.addAction(Intent.ACTION_PACKAGE_REPLACED); 114 pkgFilter.addDataScheme("package"); 115 context.registerReceiverAsUser(mReceiver, UserHandle.ALL, pkgFilter, null, null); 116 } 117 118 public void updateForPhoneId(int phoneId, String simState) { 119 log("update binding for phoneId: " + phoneId + " simState: " + simState); 120 if (!SubscriptionManager.isValidPhoneId(phoneId)) { 121 return; 122 } 123 // requires Java 7 for switch on string. 124 switch (simState) { 125 case IccCardConstants.INTENT_VALUE_ICC_ABSENT: 126 case IccCardConstants.INTENT_VALUE_ICC_CARD_IO_ERROR: 127 case IccCardConstants.INTENT_VALUE_ICC_UNKNOWN: 128 mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNBIND, mBindings[phoneId])); 129 break; 130 case IccCardConstants.INTENT_VALUE_ICC_LOADED: 131 case IccCardConstants.INTENT_VALUE_ICC_LOCKED: 132 mHandler.sendMessage(mHandler.obtainMessage(EVENT_BIND, mBindings[phoneId])); 133 break; 134 } 135 } 136 137 private class AppBinding { 138 private int phoneId; 139 private CarrierServiceConnection connection; 140 private int bindCount; 141 private long lastBindStartMillis; 142 private int unbindCount; 143 private long lastUnbindMillis; 144 private String carrierPackage; 145 146 public AppBinding(int phoneId) { 147 this.phoneId = phoneId; 148 } 149 150 public int getPhoneId() { 151 return phoneId; 152 } 153 154 public String getPackage() { 155 return carrierPackage; 156 } 157 158 public void handleConnectionDown() { 159 connection = null; 160 } 161 162 public boolean bind() { 163 // Make sure there is no existing binding for this phone 164 unbind(); 165 166 // Get the package name for the carrier app 167 List<String> carrierPackageNames = 168 TelephonyManager.from(mContext).getCarrierPackageNamesForIntentAndPhone( 169 new Intent(CarrierService.CARRIER_SERVICE_INTERFACE), phoneId 170 ); 171 172 if (carrierPackageNames == null || carrierPackageNames.size() <= 0) { 173 log("No carrier app for: " + phoneId); 174 return false; 175 } 176 177 log("Found carrier app: " + carrierPackageNames); 178 carrierPackage = carrierPackageNames.get(0); 179 180 // Log debug information 181 bindCount++; 182 lastBindStartMillis = System.currentTimeMillis(); 183 184 // Look up the carrier service 185 Intent carrierService = new Intent(CarrierService.CARRIER_SERVICE_INTERFACE); 186 carrierService.setPackage(carrierPackage); 187 188 ResolveInfo carrierResolveInfo = mContext.getPackageManager().resolveService( 189 carrierService, PackageManager.GET_META_DATA); 190 Bundle metadata = carrierResolveInfo.serviceInfo.metaData; 191 192 // Only bind if the service wants it 193 if (metadata == null || 194 !metadata.getBoolean("android.service.carrier.LONG_LIVED_BINDING", false)) { 195 log("Carrier app does not want a long lived binding"); 196 return false; 197 } 198 199 log("Binding to " + carrierPackage + " for phone " + phoneId); 200 connection = new CarrierServiceConnection(this); 201 mHandler.sendMessageDelayed( 202 mHandler.obtainMessage(EVENT_BIND_TIMEOUT, this), 203 BIND_TIMEOUT_MILLIS); 204 205 String error; 206 try { 207 if (mContext.bindService(carrierService, connection, Context.BIND_AUTO_CREATE)) { 208 return true; 209 } 210 211 error = "bindService returned false"; 212 } catch (SecurityException ex) { 213 error = ex.getMessage(); 214 } 215 216 log("Unable to bind to " + carrierPackage + " for phone " + phoneId + 217 ". Error: " + error); 218 return false; 219 } 220 221 public void unbind() { 222 mHandler.removeMessages(EVENT_BIND_TIMEOUT, this); 223 if (connection == null) { 224 return; 225 } 226 227 // Log debug information 228 unbindCount++; 229 lastUnbindMillis = System.currentTimeMillis(); 230 231 // Actually unbind 232 log("Unbinding from carrier app"); 233 mContext.unbindService(connection); 234 connection = null; 235 } 236 237 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 238 pw.println("Carrier app binding for phone " + phoneId); 239 pw.println(" connection: " + connection); 240 pw.println(" bindCount: " + bindCount); 241 pw.println(" lastBindStartMillis: " + lastBindStartMillis); 242 pw.println(" unbindCount: " + unbindCount); 243 pw.println(" lastUnbindMillis: " + lastUnbindMillis); 244 pw.println(); 245 } 246 } 247 248 private class CarrierServiceConnection implements ServiceConnection { 249 private IBinder service; 250 private AppBinding binding; 251 252 public CarrierServiceConnection(AppBinding binding) { 253 this.binding = binding; 254 } 255 256 @Override 257 public void onServiceConnected(ComponentName name, IBinder service) { 258 log("Connected to carrier app: " + name.flattenToString()); 259 mHandler.removeMessages(EVENT_BIND_TIMEOUT, binding); 260 this.service = service; 261 } 262 263 @Override 264 public void onServiceDisconnected(ComponentName name) { 265 log("Disconnected from carrier app: " + name.flattenToString()); 266 this.service = null; 267 this.binding.handleConnectionDown(); 268 } 269 } 270 271 private class PackageChangedBroadcastReceiver extends BroadcastReceiver { 272 @Override 273 public void onReceive(Context context, Intent intent) { 274 String action = intent.getAction(); 275 log("Receive action: " + action); 276 switch (action) { 277 case Intent.ACTION_PACKAGE_ADDED: 278 case Intent.ACTION_PACKAGE_REMOVED: 279 case Intent.ACTION_PACKAGE_REPLACED: 280 int uid = intent.getIntExtra(Intent.EXTRA_UID, -1); 281 String packageName = mContext.getPackageManager().getNameForUid(uid); 282 if (packageName != null) { 283 // We don't have a phoneId for arg1. 284 mHandler.sendMessage( 285 mHandler.obtainMessage(EVENT_PACKAGE_CHANGED, packageName)); 286 } 287 break; 288 289 } 290 } 291 } 292 293 private static void log(String message) { 294 Log.d(LOG_TAG, message); 295 } 296 297 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 298 pw.println("CarrierServiceBindHelper:"); 299 for (AppBinding binding : mBindings) { 300 binding.dump(fd, pw, args); 301 } 302 } 303} 304