2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.server;
19 import android.Manifest;
20 import android.app.ActivityManagerInternal;
21 import android.app.AppOpsManager;
22 import android.app.PendingIntent;
23 import android.content.ComponentName;
24 import android.content.ContentProvider;
25 import android.content.ContentValues;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.content.pm.PackageManager;
30 import android.net.Uri;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Message;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.service.carrier.CarrierMessagingService;
40 import android.telephony.SmsManager;
41 import android.telephony.TelephonyManager;
42 import android.util.Slog;
44 import com.android.internal.telephony.IMms;
46 import java.util.List;
49 * This class is a proxy for MmsService APIs. We need this because MmsService runs
50 * in phone process and may crash anytime. This manages a connection to the actual
51 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
53 public class MmsServiceBroker extends SystemService {
54 private static final String TAG = "MmsServiceBroker";
56 private static final ComponentName MMS_SERVICE_COMPONENT =
57 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
59 private static final int MSG_TRY_CONNECTING = 1;
61 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
62 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
63 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
64 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
66 private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
67 private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds
69 private Context mContext;
70 // The actual MMS service instance to invoke
71 private volatile IMms mService;
73 // Cached system service instances
74 private volatile AppOpsManager mAppOpsManager = null;
75 private volatile PackageManager mPackageManager = null;
76 private volatile TelephonyManager mTelephonyManager = null;
78 private final Handler mConnectionHandler = new Handler() {
80 public void handleMessage(Message msg) {
82 case MSG_TRY_CONNECTING:
86 Slog.e(TAG, "Unknown message");
91 private ServiceConnection mConnection = new ServiceConnection() {
93 public void onServiceConnected(ComponentName name, IBinder service) {
94 Slog.i(TAG, "MmsService connected");
95 synchronized (MmsServiceBroker.this) {
96 mService = IMms.Stub.asInterface(Binder.allowBlocking(service));
97 MmsServiceBroker.this.notifyAll();
102 public void onServiceDisconnected(ComponentName name) {
103 Slog.i(TAG, "MmsService unexpectedly disconnected");
104 synchronized (MmsServiceBroker.this) {
106 MmsServiceBroker.this.notifyAll();
108 // Retry connecting, but not too eager (with a delay)
109 // since it may come back by itself.
110 mConnectionHandler.sendMessageDelayed(
111 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING),
112 RETRY_DELAY_ON_DISCONNECTION_MS);
116 // Instance of IMms for returning failure to service API caller,
117 // used when MmsService cannot be connected.
118 private final IMms mServiceStubForFailure = new IMms() {
121 public IBinder asBinder() {
126 public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
127 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
128 returnPendingIntentWithError(sentIntent);
132 public void downloadMessage(int subId, String callingPkg, String locationUrl,
133 Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)
134 throws RemoteException {
135 returnPendingIntentWithError(downloadedIntent);
139 public Bundle getCarrierConfigValues(int subId) throws RemoteException {
144 public Uri importTextMessage(String callingPkg, String address, int type, String text,
145 long timestampMillis, boolean seen, boolean read) throws RemoteException {
150 public Uri importMultimediaMessage(String callingPkg, Uri contentUri, String messageId,
151 long timestampSecs, boolean seen, boolean read) throws RemoteException {
156 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
157 throws RemoteException {
162 public boolean deleteStoredConversation(String callingPkg, long conversationId)
163 throws RemoteException {
168 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
169 ContentValues statusValues) throws RemoteException {
174 public boolean archiveStoredConversation(String callingPkg, long conversationId,
175 boolean archived) throws RemoteException {
180 public Uri addTextMessageDraft(String callingPkg, String address, String text)
181 throws RemoteException {
186 public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
187 throws RemoteException {
192 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
193 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
194 returnPendingIntentWithError(sentIntent);
198 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
203 public boolean getAutoPersisting() throws RemoteException {
207 private void returnPendingIntentWithError(PendingIntent pendingIntent) {
209 pendingIntent.send(mContext, SmsManager.MMS_ERROR_UNSPECIFIED, null);
210 } catch (PendingIntent.CanceledException e) {
211 Slog.e(TAG, "Failed to return pending intent result", e);
216 public MmsServiceBroker(Context context) {
223 public void onStart() {
224 publishBinderService("imms", new BinderService());
227 public void systemRunning() {
228 Slog.i(TAG, "Delay connecting to MmsService until an API is called");
231 private void tryConnecting() {
232 Slog.i(TAG, "Connecting to MmsService");
233 synchronized (this) {
234 if (mService != null) {
235 Slog.d(TAG, "Already connected");
238 final Intent intent = new Intent();
239 intent.setComponent(MMS_SERVICE_COMPONENT);
241 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
242 Slog.e(TAG, "Failed to bind to MmsService");
244 } catch (SecurityException e) {
245 Slog.e(TAG, "Forbidden to bind to MmsService", e);
250 private IMms getOrConnectService() {
251 synchronized (this) {
252 if (mService != null) {
255 // Service is not connected. Try blocking connecting.
256 Slog.w(TAG, "MmsService not connected. Try connecting...");
257 mConnectionHandler.sendMessage(
258 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
259 final long shouldEnd =
260 SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
261 long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
262 while (waitTime > 0) {
264 // TODO: consider using Java concurrent construct instead of raw object wait
266 } catch (InterruptedException e) {
267 Slog.w(TAG, "Connection wait interrupted", e);
269 if (mService != null) {
273 // Calculate remaining waiting time to make sure we wait the full timeout period
274 waitTime = shouldEnd - SystemClock.elapsedRealtime();
276 // Timed out. Something's really wrong.
277 Slog.e(TAG, "Can not connect to MmsService (timed out)");
283 * Make sure to return a non-empty service instance. Return the connected MmsService
284 * instance, if not connected, try connecting. If fail to connect, return a fake service
285 * instance which returns failure to service caller.
287 * @return a non-empty service instance, real or fake
289 private IMms getServiceGuarded() {
290 final IMms service = getOrConnectService();
291 if (service != null) {
294 return mServiceStubForFailure;
297 private AppOpsManager getAppOpsManager() {
298 if (mAppOpsManager == null) {
299 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
301 return mAppOpsManager;
304 private PackageManager getPackageManager() {
305 if (mPackageManager == null) {
306 mPackageManager = mContext.getPackageManager();
308 return mPackageManager;
311 private TelephonyManager getTelephonyManager() {
312 if (mTelephonyManager == null) {
313 mTelephonyManager = (TelephonyManager) mContext.getSystemService(
314 Context.TELEPHONY_SERVICE);
316 return mTelephonyManager;
319 private String getCallingPackageName() {
320 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
321 if (packages != null && packages.length > 0) {
327 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
328 private final class BinderService extends IMms.Stub {
329 private static final String PHONE_PACKAGE_NAME = "com.android.phone";
332 public void sendMessage(int subId, String callingPkg, Uri contentUri,
333 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
334 throws RemoteException {
335 Slog.d(TAG, "sendMessage() by " + callingPkg);
336 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
337 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
338 callingPkg) != AppOpsManager.MODE_ALLOWED) {
341 contentUri = adjustUriForUserAndGrantPermission(contentUri,
342 CarrierMessagingService.SERVICE_INTERFACE,
343 Intent.FLAG_GRANT_READ_URI_PERMISSION);
344 getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
345 configOverrides, sentIntent);
349 public void downloadMessage(int subId, String callingPkg, String locationUrl,
350 Uri contentUri, Bundle configOverrides,
351 PendingIntent downloadedIntent) throws RemoteException {
352 Slog.d(TAG, "downloadMessage() by " + callingPkg);
353 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
354 "Download MMS message");
355 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
356 callingPkg) != AppOpsManager.MODE_ALLOWED) {
359 contentUri = adjustUriForUserAndGrantPermission(contentUri,
360 CarrierMessagingService.SERVICE_INTERFACE,
361 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
363 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
364 configOverrides, downloadedIntent);
368 public Bundle getCarrierConfigValues(int subId) throws RemoteException {
369 Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
370 return getServiceGuarded().getCarrierConfigValues(subId);
374 public Uri importTextMessage(String callingPkg, String address, int type, String text,
375 long timestampMillis, boolean seen, boolean read) throws RemoteException {
376 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
377 callingPkg) != AppOpsManager.MODE_ALLOWED) {
378 // Silently fail AppOps failure due to not being the default SMS app
379 // while writing the TelephonyProvider
380 return FAKE_SMS_SENT_URI;
382 return getServiceGuarded().importTextMessage(
383 callingPkg, address, type, text, timestampMillis, seen, read);
387 public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
388 String messageId, long timestampSecs, boolean seen, boolean read)
389 throws RemoteException {
390 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
391 callingPkg) != AppOpsManager.MODE_ALLOWED) {
392 // Silently fail AppOps failure due to not being the default SMS app
393 // while writing the TelephonyProvider
394 return FAKE_MMS_SENT_URI;
396 return getServiceGuarded().importMultimediaMessage(
397 callingPkg, contentUri, messageId, timestampSecs, seen, read);
401 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
402 throws RemoteException {
403 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
404 callingPkg) != AppOpsManager.MODE_ALLOWED) {
407 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
411 public boolean deleteStoredConversation(String callingPkg, long conversationId)
412 throws RemoteException {
413 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
414 callingPkg) != AppOpsManager.MODE_ALLOWED) {
417 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
421 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
422 ContentValues statusValues) throws RemoteException {
423 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
424 callingPkg) != AppOpsManager.MODE_ALLOWED) {
427 return getServiceGuarded()
428 .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
432 public boolean archiveStoredConversation(String callingPkg, long conversationId,
433 boolean archived) throws RemoteException {
434 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
435 callingPkg) != AppOpsManager.MODE_ALLOWED) {
438 return getServiceGuarded()
439 .archiveStoredConversation(callingPkg, conversationId, archived);
443 public Uri addTextMessageDraft(String callingPkg, String address, String text)
444 throws RemoteException {
445 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
446 callingPkg) != AppOpsManager.MODE_ALLOWED) {
447 // Silently fail AppOps failure due to not being the default SMS app
448 // while writing the TelephonyProvider
449 return FAKE_SMS_DRAFT_URI;
451 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
455 public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
456 throws RemoteException {
457 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
458 callingPkg) != AppOpsManager.MODE_ALLOWED) {
459 // Silently fail AppOps failure due to not being the default SMS app
460 // while writing the TelephonyProvider
461 return FAKE_MMS_DRAFT_URI;
463 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
467 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
468 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
469 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
470 callingPkg) != AppOpsManager.MODE_ALLOWED) {
473 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
478 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
479 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
480 callingPkg) != AppOpsManager.MODE_ALLOWED) {
483 getServiceGuarded().setAutoPersisting(callingPkg, enabled);
487 public boolean getAutoPersisting() throws RemoteException {
488 return getServiceGuarded().getAutoPersisting();
492 * Modifies the Uri to contain the caller's userId, if necessary.
493 * Grants the phone package on primary user permission to access the contentUri,
494 * even if the caller is not in the primary user.
496 * @param contentUri The Uri to adjust
497 * @param action The intent action used to find the associated carrier app
498 * @param permission The permission to add
499 * @return The adjusted Uri containing the calling userId.
501 private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
503 final Intent grantIntent = new Intent();
504 grantIntent.setData(contentUri);
505 grantIntent.setFlags(permission);
507 final int callingUid = Binder.getCallingUid();
508 final int callingUserId = UserHandle.getCallingUserId();
509 if (callingUserId != UserHandle.USER_SYSTEM) {
510 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
513 long token = Binder.clearCallingIdentity();
515 LocalServices.getService(ActivityManagerInternal.class)
516 .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
517 grantIntent, UserHandle.USER_SYSTEM);
519 // Grant permission for the carrier app.
520 Intent intent = new Intent(action);
521 TelephonyManager telephonyManager =
522 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
523 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
525 if (carrierPackages != null && carrierPackages.size() == 1) {
526 LocalServices.getService(ActivityManagerInternal.class)
527 .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
528 grantIntent, UserHandle.USER_SYSTEM);
531 Binder.restoreCallingIdentity(token);