1 /* 2 * Copyright (C) 2014 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.server; 18 19 import static android.telephony.SubscriptionManager.INVALID_SIM_SLOT_INDEX; 20 21 import android.Manifest; 22 import android.app.AppOpsManager; 23 import android.app.PendingIntent; 24 import android.content.ComponentName; 25 import android.content.ContentProvider; 26 import android.content.ContentValues; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.ServiceConnection; 30 import android.content.pm.PackageManager; 31 import android.net.Uri; 32 import android.os.Binder; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Message; 37 import android.os.RemoteException; 38 import android.os.SystemClock; 39 import android.os.UserHandle; 40 import android.service.carrier.CarrierMessagingService; 41 import android.telephony.SmsManager; 42 import android.telephony.SubscriptionInfo; 43 import android.telephony.SubscriptionManager; 44 import android.telephony.TelephonyManager; 45 import android.util.Slog; 46 47 import com.android.internal.telephony.IMms; 48 import com.android.internal.telephony.TelephonyPermissions; 49 import com.android.internal.telephony.util.TelephonyUtils; 50 import com.android.server.uri.NeededUriGrants; 51 import com.android.server.uri.UriGrantsManagerInternal; 52 53 import java.util.List; 54 55 /** 56 * This class is a proxy for MmsService APIs. We need this because MmsService runs 57 * in phone process and may crash anytime. This manages a connection to the actual 58 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation. 59 */ 60 public class MmsServiceBroker extends SystemService { 61 private static final String TAG = "MmsServiceBroker"; 62 63 private static final ComponentName MMS_SERVICE_COMPONENT = 64 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService"); 65 66 private static final int MSG_TRY_CONNECTING = 1; 67 68 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0"); 69 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0"); 70 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0"); 71 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0"); 72 73 private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds 74 private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds 75 76 private Context mContext; 77 // The actual MMS service instance to invoke 78 private volatile IMms mService; 79 80 // Cached system service instances 81 private volatile AppOpsManager mAppOpsManager = null; 82 private volatile PackageManager mPackageManager = null; 83 private volatile TelephonyManager mTelephonyManager = null; 84 85 private final Handler mConnectionHandler = new Handler() { 86 @Override 87 public void handleMessage(Message msg) { 88 switch (msg.what) { 89 case MSG_TRY_CONNECTING: 90 tryConnecting(); 91 break; 92 default: 93 Slog.e(TAG, "Unknown message"); 94 } 95 } 96 }; 97 98 private ServiceConnection mConnection = new ServiceConnection() { 99 @Override 100 public void onServiceConnected(ComponentName name, IBinder service) { 101 Slog.i(TAG, "MmsService connected"); 102 synchronized (MmsServiceBroker.this) { 103 mService = IMms.Stub.asInterface(Binder.allowBlocking(service)); 104 MmsServiceBroker.this.notifyAll(); 105 } 106 } 107 108 @Override 109 public void onServiceDisconnected(ComponentName name) { 110 Slog.i(TAG, "MmsService unexpectedly disconnected"); 111 synchronized (MmsServiceBroker.this) { 112 mService = null; 113 MmsServiceBroker.this.notifyAll(); 114 } 115 // Retry connecting, but not too eager (with a delay) 116 // since it may come back by itself. 117 mConnectionHandler.sendMessageDelayed( 118 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING), 119 RETRY_DELAY_ON_DISCONNECTION_MS); 120 } 121 }; 122 123 // Instance of IMms for returning failure to service API caller, 124 // used when MmsService cannot be connected. 125 private final IMms mServiceStubForFailure = new IMms() { 126 127 @Override 128 public IBinder asBinder() { 129 return null; 130 } 131 132 @Override 133 public void sendMessage(int subId, int callingUser, String callingPkg, 134 Uri contentUri, String locationUrl, Bundle configOverrides, 135 PendingIntent sentIntent, long messageId, 136 String attributionTag) throws RemoteException { 137 returnPendingIntentWithError(sentIntent); 138 } 139 140 @Override 141 public void downloadMessage(int subId, int callingUser, String callingPkg, 142 String locationUrl, Uri contentUri, Bundle configOverrides, 143 PendingIntent downloadedIntent, 144 long messageId, String attributionTag) throws RemoteException { 145 returnPendingIntentWithError(downloadedIntent); 146 } 147 148 @Override 149 public Uri importTextMessage(String callingPkg, String address, int type, String text, 150 long timestampMillis, boolean seen, boolean read) throws RemoteException { 151 return null; 152 } 153 154 @Override 155 public Uri importMultimediaMessage(int callingUser, String callingPkg, 156 Uri contentUri, String messageId, long timestampSecs, 157 boolean seen, boolean read) throws RemoteException { 158 return null; 159 } 160 161 @Override 162 public boolean deleteStoredMessage(String callingPkg, Uri messageUri) 163 throws RemoteException { 164 return false; 165 } 166 167 @Override 168 public boolean deleteStoredConversation(String callingPkg, long conversationId) 169 throws RemoteException { 170 return false; 171 } 172 173 @Override 174 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri, 175 ContentValues statusValues) throws RemoteException { 176 return false; 177 } 178 179 @Override 180 public boolean archiveStoredConversation(String callingPkg, long conversationId, 181 boolean archived) throws RemoteException { 182 return false; 183 } 184 185 @Override 186 public Uri addTextMessageDraft(String callingPkg, String address, String text) 187 throws RemoteException { 188 return null; 189 } 190 191 @Override 192 public Uri addMultimediaMessageDraft(int callingUser, String callingPkg, 193 Uri contentUri) throws RemoteException { 194 return null; 195 } 196 197 @Override 198 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri, 199 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { 200 returnPendingIntentWithError(sentIntent); 201 } 202 203 @Override 204 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException { 205 // Do nothing 206 } 207 208 @Override 209 public boolean getAutoPersisting() throws RemoteException { 210 return false; 211 } 212 213 private void returnPendingIntentWithError(PendingIntent pendingIntent) { 214 try { 215 pendingIntent.send(mContext, SmsManager.MMS_ERROR_UNSPECIFIED, null); 216 } catch (PendingIntent.CanceledException e) { 217 Slog.e(TAG, "Failed to return pending intent result", e); 218 } 219 } 220 }; 221 MmsServiceBroker(Context context)222 public MmsServiceBroker(Context context) { 223 super(context); 224 mContext = context; 225 mService = null; 226 } 227 228 @Override onStart()229 public void onStart() { 230 publishBinderService("imms", new BinderService()); 231 } 232 systemRunning()233 public void systemRunning() { 234 Slog.i(TAG, "Delay connecting to MmsService until an API is called"); 235 } 236 tryConnecting()237 private void tryConnecting() { 238 Slog.i(TAG, "Connecting to MmsService"); 239 synchronized (this) { 240 if (mService != null) { 241 Slog.d(TAG, "Already connected"); 242 return; 243 } 244 final Intent intent = new Intent(); 245 intent.setComponent(MMS_SERVICE_COMPONENT); 246 try { 247 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) { 248 Slog.e(TAG, "Failed to bind to MmsService"); 249 } 250 } catch (SecurityException e) { 251 Slog.e(TAG, "Forbidden to bind to MmsService", e); 252 } 253 } 254 } 255 getOrConnectService()256 private IMms getOrConnectService() { 257 synchronized (this) { 258 if (mService != null) { 259 return mService; 260 } 261 // Service is not connected. Try blocking connecting. 262 Slog.w(TAG, "MmsService not connected. Try connecting..."); 263 mConnectionHandler.sendMessage( 264 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING)); 265 final long shouldEnd = 266 SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS; 267 long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS; 268 while (waitTime > 0) { 269 try { 270 // TODO: consider using Java concurrent construct instead of raw object wait 271 this.wait(waitTime); 272 } catch (InterruptedException e) { 273 Slog.w(TAG, "Connection wait interrupted", e); 274 } 275 if (mService != null) { 276 // Success 277 return mService; 278 } 279 // Calculate remaining waiting time to make sure we wait the full timeout period 280 waitTime = shouldEnd - SystemClock.elapsedRealtime(); 281 } 282 // Timed out. Something's really wrong. 283 Slog.e(TAG, "Can not connect to MmsService (timed out)"); 284 return null; 285 } 286 } 287 288 /** 289 * Make sure to return a non-empty service instance. Return the connected MmsService 290 * instance, if not connected, try connecting. If fail to connect, return a fake service 291 * instance which returns failure to service caller. 292 * 293 * @return a non-empty service instance, real or fake 294 */ getServiceGuarded()295 private IMms getServiceGuarded() { 296 final IMms service = getOrConnectService(); 297 if (service != null) { 298 return service; 299 } 300 return mServiceStubForFailure; 301 } 302 getAppOpsManager()303 private AppOpsManager getAppOpsManager() { 304 if (mAppOpsManager == null) { 305 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); 306 } 307 return mAppOpsManager; 308 } 309 getPackageManager()310 private PackageManager getPackageManager() { 311 if (mPackageManager == null) { 312 mPackageManager = mContext.getPackageManager(); 313 } 314 return mPackageManager; 315 } 316 getTelephonyManager()317 private TelephonyManager getTelephonyManager() { 318 if (mTelephonyManager == null) { 319 mTelephonyManager = (TelephonyManager) mContext.getSystemService( 320 Context.TELEPHONY_SERVICE); 321 } 322 return mTelephonyManager; 323 } 324 getCallingPackageName()325 private String getCallingPackageName() { 326 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid()); 327 if (packages != null && packages.length > 0) { 328 return packages[0]; 329 } 330 return "unknown"; 331 } 332 333 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service" 334 private final class BinderService extends IMms.Stub { 335 private static final String PHONE_PACKAGE_NAME = "com.android.phone"; 336 337 @Override sendMessage(int subId, int callingUser, String callingPkg, Uri contentUri, String locationUrl, Bundle configOverrides, PendingIntent sentIntent, long messageId, String attributionTag)338 public void sendMessage(int subId, int callingUser, String callingPkg, 339 Uri contentUri, String locationUrl, Bundle configOverrides, 340 PendingIntent sentIntent, long messageId, String attributionTag) 341 throws RemoteException { 342 Slog.d(TAG, "sendMessage() by " + callingPkg); 343 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message"); 344 345 // Check if user is associated with the subscription 346 if (!TelephonyPermissions.checkSubscriptionAssociatedWithUser(mContext, subId, 347 Binder.getCallingUserHandle()) 348 // For inactive sub, fall through to MMS service to have it recorded in metrics. 349 && isActiveSubId(subId)) { 350 // Try remind user to use another profile to send. 351 TelephonyUtils.showSwitchToManagedProfileDialogIfAppropriate(mContext, 352 subId, Binder.getCallingUid(), callingPkg); 353 return; 354 } 355 356 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 357 callingPkg, attributionTag, null) != AppOpsManager.MODE_ALLOWED) { 358 Slog.e(TAG, callingPkg + " is not allowed to call sendMessage()"); 359 return; 360 } 361 contentUri = adjustUriForUserAndGrantPermission(contentUri, 362 CarrierMessagingService.SERVICE_INTERFACE, 363 Intent.FLAG_GRANT_READ_URI_PERMISSION, 364 subId); 365 getServiceGuarded().sendMessage(subId, getCallingUserId(), callingPkg, contentUri, 366 locationUrl, configOverrides, sentIntent, messageId, attributionTag); 367 } 368 369 @Override downloadMessage(int subId, int callingUser, String callingPkg, String locationUrl, Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent, long messageId, String attributionTag)370 public void downloadMessage(int subId, int callingUser, String callingPkg, 371 String locationUrl, Uri contentUri, Bundle configOverrides, 372 PendingIntent downloadedIntent, long messageId, String attributionTag) 373 throws RemoteException { 374 Slog.d(TAG, "downloadMessage() by " + callingPkg); 375 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS, 376 "Download MMS message"); 377 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(), 378 callingPkg, attributionTag, null) != AppOpsManager.MODE_ALLOWED) { 379 Slog.e(TAG, callingPkg + " is not allowed to call downloadMessage()"); 380 return; 381 } 382 contentUri = adjustUriForUserAndGrantPermission(contentUri, 383 CarrierMessagingService.SERVICE_INTERFACE, 384 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION, 385 subId); 386 387 getServiceGuarded().downloadMessage(subId, getCallingUserId(), callingPkg, locationUrl, 388 contentUri, configOverrides, downloadedIntent, messageId, attributionTag); 389 } 390 391 @Override importTextMessage(String callingPkg, String address, int type, String text, long timestampMillis, boolean seen, boolean read)392 public Uri importTextMessage(String callingPkg, String address, int type, String text, 393 long timestampMillis, boolean seen, boolean read) throws RemoteException { 394 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 395 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 396 // Silently fail AppOps failure due to not being the default SMS app 397 // while writing the TelephonyProvider 398 return FAKE_SMS_SENT_URI; 399 } 400 return getServiceGuarded().importTextMessage( 401 callingPkg, address, type, text, timestampMillis, seen, read); 402 } 403 404 @Override importMultimediaMessage(int callingUser, String callingPkg, Uri contentUri, String messageId, long timestampSecs, boolean seen, boolean read)405 public Uri importMultimediaMessage(int callingUser, String callingPkg, 406 Uri contentUri, String messageId, long timestampSecs, boolean seen, boolean read) 407 throws RemoteException { 408 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 409 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 410 // Silently fail AppOps failure due to not being the default SMS app 411 // while writing the TelephonyProvider 412 return FAKE_MMS_SENT_URI; 413 } 414 return getServiceGuarded().importMultimediaMessage(getCallingUserId(), callingPkg, 415 contentUri, messageId, timestampSecs, seen, read); 416 } 417 418 @Override deleteStoredMessage(String callingPkg, Uri messageUri)419 public boolean deleteStoredMessage(String callingPkg, Uri messageUri) 420 throws RemoteException { 421 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 422 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 423 return false; 424 } 425 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri); 426 } 427 428 @Override deleteStoredConversation(String callingPkg, long conversationId)429 public boolean deleteStoredConversation(String callingPkg, long conversationId) 430 throws RemoteException { 431 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 432 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 433 return false; 434 } 435 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId); 436 } 437 438 @Override updateStoredMessageStatus(String callingPkg, Uri messageUri, ContentValues statusValues)439 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri, 440 ContentValues statusValues) throws RemoteException { 441 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 442 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 443 return false; 444 } 445 return getServiceGuarded() 446 .updateStoredMessageStatus(callingPkg, messageUri, statusValues); 447 } 448 449 @Override archiveStoredConversation(String callingPkg, long conversationId, boolean archived)450 public boolean archiveStoredConversation(String callingPkg, long conversationId, 451 boolean archived) throws RemoteException { 452 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 453 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 454 return false; 455 } 456 return getServiceGuarded() 457 .archiveStoredConversation(callingPkg, conversationId, archived); 458 } 459 460 @Override addTextMessageDraft(String callingPkg, String address, String text)461 public Uri addTextMessageDraft(String callingPkg, String address, String text) 462 throws RemoteException { 463 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 464 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 465 // Silently fail AppOps failure due to not being the default SMS app 466 // while writing the TelephonyProvider 467 return FAKE_SMS_DRAFT_URI; 468 } 469 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text); 470 } 471 472 @Override addMultimediaMessageDraft(int callingUser, String callingPkg, Uri contentUri)473 public Uri addMultimediaMessageDraft(int callingUser, String callingPkg, 474 Uri contentUri) throws RemoteException { 475 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 476 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 477 // Silently fail AppOps failure due to not being the default SMS app 478 // while writing the TelephonyProvider 479 return FAKE_MMS_DRAFT_URI; 480 } 481 return getServiceGuarded().addMultimediaMessageDraft(getCallingUserId(), callingPkg, 482 contentUri); 483 } 484 485 @Override sendStoredMessage(int subId, String callingPkg, Uri messageUri, Bundle configOverrides, PendingIntent sentIntent)486 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri, 487 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException { 488 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(), 489 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 490 return; 491 } 492 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides, 493 sentIntent); 494 } 495 496 @Override setAutoPersisting(String callingPkg, boolean enabled)497 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException { 498 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(), 499 callingPkg, null, null) != AppOpsManager.MODE_ALLOWED) { 500 return; 501 } 502 getServiceGuarded().setAutoPersisting(callingPkg, enabled); 503 } 504 505 @Override getAutoPersisting()506 public boolean getAutoPersisting() throws RemoteException { 507 return getServiceGuarded().getAutoPersisting(); 508 } 509 510 /** 511 * Modifies the Uri to contain the caller's userId, if necessary. 512 * Grants the phone package on primary user permission to access the contentUri, 513 * even if the caller is not in the primary user. 514 * 515 * @param contentUri The Uri to adjust 516 * @param action The intent action used to find the associated carrier app 517 * @param permission The permission to add 518 * @return The adjusted Uri containing the calling userId. 519 */ adjustUriForUserAndGrantPermission(Uri contentUri, String action, int permission, int subId)520 private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action, 521 int permission, int subId) { 522 final Intent grantIntent = new Intent(); 523 grantIntent.setData(contentUri); 524 grantIntent.setFlags(permission); 525 526 final int callingUid = Binder.getCallingUid(); 527 final int callingUserId = UserHandle.getCallingUserId(); 528 if (callingUserId != UserHandle.USER_SYSTEM) { 529 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId); 530 } 531 532 final long token = Binder.clearCallingIdentity(); 533 try { 534 final UriGrantsManagerInternal ugm = LocalServices 535 .getService(UriGrantsManagerInternal.class); 536 final NeededUriGrants needed = ugm.checkGrantUriPermissionFromIntent( 537 grantIntent, callingUid, PHONE_PACKAGE_NAME, UserHandle.USER_SYSTEM); 538 ugm.grantUriPermissionUncheckedFromIntent(needed, null); 539 540 // Grant permission for the carrier app. 541 Intent intent = new Intent(action); 542 TelephonyManager telephonyManager = (TelephonyManager) 543 mContext.getSystemService(Context.TELEPHONY_SERVICE); 544 List<String> carrierPackages = telephonyManager 545 .getCarrierPackageNamesForIntentAndPhone( 546 intent, getPhoneIdFromSubId(subId)); 547 if (carrierPackages != null && carrierPackages.size() == 1) { 548 final NeededUriGrants carrierNeeded = ugm.checkGrantUriPermissionFromIntent( 549 grantIntent, callingUid, carrierPackages.get(0), 550 UserHandle.USER_SYSTEM); 551 ugm.grantUriPermissionUncheckedFromIntent(carrierNeeded, null); 552 } 553 } finally { 554 Binder.restoreCallingIdentity(token); 555 } 556 return contentUri; 557 } 558 } 559 560 /** @return true if the subId is active. */ isActiveSubId(int subId)561 private boolean isActiveSubId(int subId) { 562 final long token = Binder.clearCallingIdentity(); 563 try { 564 SubscriptionManager subManager = mContext.getSystemService(SubscriptionManager.class); 565 return subManager != null && subManager.isActiveSubscriptionId(subId); 566 } finally { 567 Binder.restoreCallingIdentity(token); 568 } 569 } 570 getPhoneIdFromSubId(int subId)571 private int getPhoneIdFromSubId(int subId) { 572 SubscriptionManager subManager = (SubscriptionManager) 573 mContext.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE); 574 if (subManager == null) return INVALID_SIM_SLOT_INDEX; 575 SubscriptionInfo info = subManager.getActiveSubscriptionInfo(subId); 576 if (info == null) return INVALID_SIM_SLOT_INDEX; 577 return info.getSimSlotIndex(); 578 } 579 580 /** 581 * Retrieves the calling user id. 582 * @return The id of the calling user. 583 */ getCallingUserId()584 private int getCallingUserId() { 585 return Binder.getCallingUserHandle().getIdentifier(); 586 } 587 588 } 589