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