1 /* 2 * Copyright 2020 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.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.Build; 24 import android.os.Handler; 25 import android.os.HandlerThread; 26 import android.os.IBinder; 27 import android.os.Looper; 28 import android.os.Message; 29 import android.os.RemoteException; 30 import android.telephony.IBootstrapAuthenticationCallback; 31 import android.telephony.SubscriptionManager; 32 import android.telephony.TelephonyManager; 33 import android.telephony.gba.GbaAuthRequest; 34 import android.telephony.gba.GbaService; 35 import android.telephony.gba.IGbaService; 36 import android.text.TextUtils; 37 import android.util.SparseArray; 38 39 import com.android.internal.annotations.VisibleForTesting; 40 import com.android.internal.telephony.metrics.RcsStats; 41 import com.android.telephony.Rlog; 42 43 import java.util.concurrent.ConcurrentLinkedQueue; 44 45 /** 46 * Class that serves as the layer between GbaService and ServiceStateTracker. It helps binding, 47 * sending request, receiving callback, and registering for state change to GbaService. 48 */ 49 public class GbaManager { 50 private static final boolean DBG = Build.IS_DEBUGGABLE; 51 private static final int EVENT_BIND_SERVICE = 1; 52 private static final int EVENT_UNBIND_SERVICE = 2; 53 private static final int EVENT_BIND_FAIL = 3; 54 private static final int EVENT_BIND_SUCCESS = 4; 55 private static final int EVENT_CONFIG_CHANGED = 5; 56 private static final int EVENT_REQUESTS_RECEIVED = 6; 57 58 @VisibleForTesting 59 public static final int RETRY_TIME_MS = 3000; 60 @VisibleForTesting 61 public static final int MAX_RETRY = 5; 62 @VisibleForTesting 63 public static final int REQUEST_TIMEOUT_MS = 5000; 64 private final RcsStats mRcsStats; 65 66 private final String mLogTag; 67 private final Context mContext; 68 private final int mSubId; 69 70 private IGbaService mIGbaService; 71 private GbaDeathRecipient mDeathRecipient; 72 private String mTargetBindingPackageName; 73 private GbaServiceConnection mServiceConnection; 74 private Handler mHandler; 75 76 private String mServicePackageName; 77 private String mServicePackageNameOverride; 78 private int mReleaseTime; 79 private int mRetryTimes = 0; 80 81 //the requests to be sent to the GBA service 82 private final ConcurrentLinkedQueue<GbaAuthRequest> mRequestQueue = 83 new ConcurrentLinkedQueue<>(); 84 //the callbacks of the pending requests which have been sent to the GBA service 85 private final SparseArray<IBootstrapAuthenticationCallback> mCallbacks = new SparseArray<>(); 86 87 private static final SparseArray<GbaManager> sGbaManagers = new SparseArray<>(); 88 89 private final class GbaManagerHandler extends Handler { GbaManagerHandler(Looper looper)90 GbaManagerHandler(Looper looper) { 91 super(looper); 92 } 93 94 @Override handleMessage(Message msg)95 public void handleMessage(Message msg) { 96 logv("handle msg:" + msg.what); 97 switch (msg.what) { 98 case EVENT_BIND_SERVICE: 99 if (mRetryTimes++ < MAX_RETRY) { 100 rebindService(false); 101 } else { 102 loge("Too many retries, stop now!"); 103 sendEmptyMessage(EVENT_BIND_FAIL); 104 } 105 break; 106 case EVENT_UNBIND_SERVICE: 107 //do nothing if new requests are coming 108 if (mRequestQueue.isEmpty()) { 109 clearCallbacksAndNotifyFailure(); 110 unbindService(); 111 } 112 break; 113 case EVENT_BIND_FAIL: 114 case EVENT_BIND_SUCCESS: 115 mRetryTimes = 0; 116 processRequests(); 117 break; 118 case EVENT_REQUESTS_RECEIVED: 119 if (isServiceConnected()) { 120 processRequests(); 121 } else { 122 if (!mHandler.hasMessages(EVENT_BIND_SERVICE)) { 123 mHandler.sendEmptyMessage(EVENT_BIND_SERVICE); 124 } 125 } 126 break; 127 case EVENT_CONFIG_CHANGED: 128 mRetryTimes = 0; 129 if (isServiceConnetable() || isServiceConnected()) { 130 //force to rebind when config is changed 131 rebindService(true); 132 } 133 break; 134 default: 135 loge("Unhandled event " + msg.what); 136 } 137 } 138 } 139 140 private final class GbaDeathRecipient implements IBinder.DeathRecipient { 141 142 private final ComponentName mComponentName; 143 private IBinder mBinder; 144 GbaDeathRecipient(ComponentName name)145 GbaDeathRecipient(ComponentName name) { 146 mComponentName = name; 147 } 148 linkToDeath(IBinder service)149 public void linkToDeath(IBinder service) throws RemoteException { 150 if (service != null) { 151 mBinder = service; 152 mBinder.linkToDeath(this, 0); 153 } 154 } 155 unlinkToDeath()156 public synchronized void unlinkToDeath() { 157 if (mBinder != null) { 158 mBinder.unlinkToDeath(this, 0); 159 mBinder = null; 160 } 161 } 162 163 @Override binderDied()164 public void binderDied() { 165 logd("GbaService(" + mComponentName + ") has died."); 166 unlinkToDeath(); 167 //retry if died 168 retryBind(); 169 } 170 } 171 172 private final class GbaServiceConnection implements ServiceConnection { 173 @Override onServiceConnected(ComponentName name, IBinder service)174 public void onServiceConnected(ComponentName name, IBinder service) { 175 logd("service " + name + " for Gba is connected."); 176 mIGbaService = IGbaService.Stub.asInterface(service); 177 mDeathRecipient = new GbaDeathRecipient(name); 178 try { 179 mDeathRecipient.linkToDeath(service); 180 } catch (RemoteException exception) { 181 // Remote exception means that the binder already died. 182 mDeathRecipient.binderDied(); 183 logd("RemoteException " + exception); 184 } 185 mHandler.sendEmptyMessage(EVENT_BIND_SUCCESS); 186 } 187 188 @Override onServiceDisconnected(ComponentName name)189 public void onServiceDisconnected(ComponentName name) { 190 logd("service " + name + " is now disconnected."); 191 mTargetBindingPackageName = null; 192 } 193 } 194 195 @VisibleForTesting GbaManager(Context context, int subId, String servicePackageName, int releaseTime, RcsStats rcsStats)196 public GbaManager(Context context, int subId, String servicePackageName, int releaseTime, 197 RcsStats rcsStats) { 198 mContext = context; 199 mSubId = subId; 200 mLogTag = "GbaManager[" + subId + "]"; 201 202 mServicePackageName = servicePackageName; 203 mReleaseTime = releaseTime; 204 205 HandlerThread headlerThread = new HandlerThread(mLogTag); 206 headlerThread.start(); 207 mHandler = new GbaManagerHandler(headlerThread.getLooper()); 208 209 if (mReleaseTime < 0) { 210 mHandler.sendEmptyMessage(EVENT_BIND_SERVICE); 211 } 212 mRcsStats = rcsStats; 213 } 214 215 /** 216 * create a GbaManager instance for a sub 217 */ make(Context context, int subId, String servicePackageName, int releaseTime)218 public static GbaManager make(Context context, int subId, 219 String servicePackageName, int releaseTime) { 220 GbaManager gm = new GbaManager(context, subId, servicePackageName, releaseTime, 221 RcsStats.getInstance()); 222 synchronized (sGbaManagers) { 223 sGbaManagers.put(subId, gm); 224 } 225 return gm; 226 } 227 228 /** 229 * get singleton instance of GbaManager 230 * @return GbaManager 231 */ getInstance(int subId)232 public static GbaManager getInstance(int subId) { 233 synchronized (sGbaManagers) { 234 return sGbaManagers.get(subId); 235 } 236 } 237 238 /** 239 * handle the bootstrap authentication request 240 * @hide 241 */ bootstrapAuthenticationRequest(GbaAuthRequest req)242 public void bootstrapAuthenticationRequest(GbaAuthRequest req) { 243 logv("bootstrapAuthenticationRequest: " + req); 244 //No GBA service configured 245 if (TextUtils.isEmpty(getServicePackage())) { 246 logd("do not support!"); 247 try { 248 req.getCallback().onAuthenticationFailure(req.getToken(), 249 TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); 250 } catch (RemoteException exception) { 251 loge("exception to call service: " + exception); 252 } 253 return; 254 } 255 256 mRequestQueue.offer(req); 257 if (!mHandler.hasMessages(EVENT_REQUESTS_RECEIVED)) { 258 mHandler.sendEmptyMessage(EVENT_REQUESTS_RECEIVED); 259 } 260 } 261 262 private final IBootstrapAuthenticationCallback mServiceCallback = 263 new IBootstrapAuthenticationCallback.Stub() { 264 @Override 265 public void onKeysAvailable(int token, byte[] gbaKey, String btId) { 266 logv("onKeysAvailable: " + Integer.toHexString(token) + ", id: " + btId); 267 268 IBootstrapAuthenticationCallback cb = null; 269 synchronized (mCallbacks) { 270 cb = mCallbacks.get(token); 271 } 272 if (cb != null) { 273 try { 274 cb.onKeysAvailable(token, gbaKey, btId); 275 mRcsStats.onGbaSuccessEvent(mSubId); 276 } catch (RemoteException exception) { 277 logd("RemoteException " + exception); 278 } 279 synchronized (mCallbacks) { 280 mCallbacks.remove(token); 281 if (mCallbacks.size() == 0) { 282 releaseServiceAsNeeded(0); 283 } 284 } 285 } 286 } 287 288 @Override 289 public void onAuthenticationFailure(int token, int reason) { 290 logd("onAuthenticationFailure: " 291 + Integer.toHexString(token) + " for: " + reason); 292 293 IBootstrapAuthenticationCallback cb = null; 294 synchronized (mCallbacks) { 295 cb = mCallbacks.get(token); 296 } 297 if (cb != null) { 298 try { 299 cb.onAuthenticationFailure(token, reason); 300 mRcsStats.onGbaFailureEvent(mSubId, reason); 301 } catch (RemoteException exception) { 302 logd("RemoteException " + exception); 303 } 304 synchronized (mCallbacks) { 305 mCallbacks.remove(token); 306 if (mCallbacks.size() == 0) { 307 releaseServiceAsNeeded(0); 308 } 309 } 310 } 311 } 312 }; 313 processRequests()314 private void processRequests() { 315 if (isServiceConnected()) { 316 try { 317 while (!mRequestQueue.isEmpty()) { 318 GbaAuthRequest request = new GbaAuthRequest(mRequestQueue.peek()); 319 synchronized (mCallbacks) { 320 mCallbacks.put(request.getToken(), request.getCallback()); 321 } 322 request.setCallback(mServiceCallback); 323 mIGbaService.authenticationRequest(request); 324 mRequestQueue.poll(); 325 } 326 } catch (RemoteException exception) { 327 // Remote exception means that the binder already died. 328 mDeathRecipient.binderDied(); 329 logd("RemoteException " + exception); 330 } 331 } else { 332 while (!mRequestQueue.isEmpty()) { 333 GbaAuthRequest req = mRequestQueue.poll(); 334 try { 335 req.getCallback().onAuthenticationFailure(req.getToken(), 336 TelephonyManager.GBA_FAILURE_REASON_FEATURE_NOT_SUPPORTED); 337 } catch (RemoteException exception) { 338 logd("RemoteException " + exception); 339 } 340 } 341 } 342 343 releaseServiceAsNeeded(REQUEST_TIMEOUT_MS); 344 } 345 releaseServiceAsNeeded(int timeout)346 private void releaseServiceAsNeeded(int timeout) { 347 int configReleaseTime = getReleaseTime(); 348 //always on 349 if (configReleaseTime < 0) { 350 return; 351 } 352 //schedule to release service 353 int delayTime = configReleaseTime > timeout ? configReleaseTime : timeout; 354 if (mHandler.hasMessages(EVENT_UNBIND_SERVICE)) { 355 mHandler.removeMessages(EVENT_UNBIND_SERVICE); 356 } 357 mHandler.sendEmptyMessageDelayed(EVENT_UNBIND_SERVICE, delayTime); 358 } 359 clearCallbacksAndNotifyFailure()360 private void clearCallbacksAndNotifyFailure() { 361 synchronized (mCallbacks) { 362 for (int i = 0; i < mCallbacks.size(); i++) { 363 IBootstrapAuthenticationCallback cb = mCallbacks.valueAt(i); 364 try { 365 cb.onAuthenticationFailure(mCallbacks.keyAt(i), 366 TelephonyManager.GBA_FAILURE_REASON_UNKNOWN); 367 } catch (RemoteException exception) { 368 logd("RemoteException " + exception); 369 } 370 } 371 mCallbacks.clear(); 372 } 373 } 374 375 /** return if GBA service has been connected */ 376 @VisibleForTesting isServiceConnected()377 public boolean isServiceConnected() { 378 //current bound service should be the updated service package. 379 synchronized (this) { 380 return (mIGbaService != null) && (mIGbaService.asBinder().isBinderAlive()) 381 && TextUtils.equals(mServicePackageName, mTargetBindingPackageName); 382 } 383 } 384 isServiceConnetable()385 private boolean isServiceConnetable() { 386 synchronized (this) { 387 return mTargetBindingPackageName != null || ( 388 mReleaseTime < 0 && !TextUtils.isEmpty(mServicePackageName)); 389 } 390 } 391 unbindService()392 private void unbindService() { 393 if (mDeathRecipient != null) { 394 mDeathRecipient.unlinkToDeath(); 395 } 396 if (mServiceConnection != null) { 397 logv("unbind service."); 398 mContext.unbindService(mServiceConnection); 399 } 400 mDeathRecipient = null; 401 mIGbaService = null; 402 mServiceConnection = null; 403 mTargetBindingPackageName = null; 404 } 405 bindService()406 private void bindService() { 407 if (mContext == null || !SubscriptionManager.isValidSubscriptionId(mSubId)) { 408 loge("Can't bind service with invalid sub Id."); 409 return; 410 } 411 412 String servicePackage = getServicePackage(); 413 if (TextUtils.isEmpty(servicePackage)) { 414 loge("Can't find the binding package"); 415 return; 416 } 417 418 Intent intent = new Intent(GbaService.SERVICE_INTERFACE); 419 intent.setPackage(servicePackage); 420 421 try { 422 logv("Trying to bind " + servicePackage); 423 mServiceConnection = new GbaServiceConnection(); 424 if (!mContext.bindService(intent, mServiceConnection, 425 Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE)) { 426 logd("Cannot bind to the service."); 427 retryBind(); 428 return; 429 } 430 mTargetBindingPackageName = servicePackage; 431 } catch (SecurityException exception) { 432 loge("bindService failed " + exception); 433 } 434 } 435 retryBind()436 private void retryBind() { 437 //do nothing if binding service has been scheduled 438 if (mHandler.hasMessages(EVENT_BIND_SERVICE)) { 439 logv("wait for pending retry."); 440 return; 441 } 442 443 logv("starting retry:" + mRetryTimes); 444 445 mHandler.sendEmptyMessageDelayed(EVENT_BIND_SERVICE, RETRY_TIME_MS); 446 } 447 rebindService(boolean isForce)448 private void rebindService(boolean isForce) { 449 // Do nothing if no need to rebind. 450 if (!isForce && isServiceConnected()) { 451 logv("Service " + getServicePackage() + " already bound or being bound."); 452 return; 453 } 454 455 unbindService(); 456 bindService(); 457 } 458 459 /** override GBA service package name to be connected */ overrideServicePackage(String packageName)460 public boolean overrideServicePackage(String packageName) { 461 synchronized (this) { 462 if (!TextUtils.equals(mServicePackageName, packageName)) { 463 logv("Service package name is changed from " + mServicePackageName 464 + " to " + packageName); 465 mServicePackageName = packageName; 466 if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) { 467 mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED); 468 } 469 return true; 470 } 471 } 472 return false; 473 } 474 475 /** return GBA service package name */ getServicePackage()476 public String getServicePackage() { 477 synchronized (this) { 478 return mServicePackageName; 479 } 480 } 481 482 /** override the release time to unbind GBA service after the request is handled */ overrideReleaseTime(int interval)483 public boolean overrideReleaseTime(int interval) { 484 synchronized (this) { 485 if (mReleaseTime != interval) { 486 logv("Service release time is changed from " + mReleaseTime 487 + " to " + interval); 488 mReleaseTime = interval; 489 if (!mHandler.hasMessages(EVENT_CONFIG_CHANGED)) { 490 mHandler.sendEmptyMessage(EVENT_CONFIG_CHANGED); 491 } 492 return true; 493 } 494 } 495 return false; 496 } 497 498 /** return the release time to unbind GBA service after the request is handled */ getReleaseTime()499 public int getReleaseTime() { 500 synchronized (this) { 501 return mReleaseTime; 502 } 503 } 504 505 @VisibleForTesting getHandler()506 public Handler getHandler() { 507 return mHandler; 508 } 509 510 /** only for testing */ 511 @VisibleForTesting destroy()512 public void destroy() { 513 mHandler.removeCallbacksAndMessages(null); 514 mHandler.getLooper().quit(); 515 mRequestQueue.clear(); 516 mCallbacks.clear(); 517 unbindService(); 518 sGbaManagers.remove(mSubId); 519 } 520 logv(String msg)521 private void logv(String msg) { 522 if (DBG) { 523 Rlog.d(mLogTag, msg); 524 } 525 } 526 logd(String msg)527 private void logd(String msg) { 528 Rlog.d(mLogTag, msg); 529 } 530 loge(String msg)531 private void loge(String msg) { 532 Rlog.e(mLogTag, msg); 533 } 534 } 535