blob: 0de6a03144b5bff44fb8133d079d2b8e1400a143 [file] [log] [blame]
Ye Wend97e1fd2014-07-24 12:56:45 -07001/*
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
17package com.android.server;
18
Ye Wend97e1fd2014-07-24 12:56:45 -070019import android.Manifest;
20import android.app.AppOpsManager;
21import android.app.PendingIntent;
22import android.content.ComponentName;
Tom Taylor86201db2014-11-24 09:36:43 -080023import android.content.ContentProvider;
Ye Wend97e1fd2014-07-24 12:56:45 -070024import android.content.ContentValues;
25import android.content.Context;
26import android.content.Intent;
27import android.content.ServiceConnection;
28import android.content.pm.PackageManager;
29import android.net.Uri;
30import android.os.Binder;
Shri Borde72379722014-09-02 09:48:49 -070031import android.os.Bundle;
Ye Wend97e1fd2014-07-24 12:56:45 -070032import android.os.Handler;
33import android.os.IBinder;
34import android.os.Message;
35import android.os.RemoteException;
Ye Wen724dbbd72014-10-07 15:33:51 -070036import android.os.SystemClock;
Tom Taylor86201db2014-11-24 09:36:43 -080037import android.os.UserHandle;
Cheuksan Wang5cec9202014-12-16 13:40:36 -080038import android.service.carrier.CarrierMessagingService;
Ye Wend97e1fd2014-07-24 12:56:45 -070039import android.telephony.TelephonyManager;
40import android.util.Slog;
41
Ye Wenbdc3a462014-11-11 11:17:28 -080042import com.android.internal.telephony.IMms;
43
Tom Taylor86201db2014-11-24 09:36:43 -080044import java.util.List;
45
Ye Wend97e1fd2014-07-24 12:56:45 -070046/**
47 * This class is a proxy for MmsService APIs. We need this because MmsService runs
48 * in phone process and may crash anytime. This manages a connection to the actual
49 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
50 */
51public class MmsServiceBroker extends SystemService {
52 private static final String TAG = "MmsServiceBroker";
53
54 private static final ComponentName MMS_SERVICE_COMPONENT =
55 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
56
57 private static final int MSG_TRY_CONNECTING = 1;
58
Ye Wenfa58ac02014-07-31 17:15:30 -070059 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
60 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
61 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
62 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
63
Ye Wen724dbbd72014-10-07 15:33:51 -070064 private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
65 private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds
66
Ye Wend97e1fd2014-07-24 12:56:45 -070067 private Context mContext;
68 // The actual MMS service instance to invoke
69 private volatile IMms mService;
Ye Wend97e1fd2014-07-24 12:56:45 -070070
71 // Cached system service instances
72 private volatile AppOpsManager mAppOpsManager = null;
73 private volatile PackageManager mPackageManager = null;
74 private volatile TelephonyManager mTelephonyManager = null;
75
76 private final Handler mConnectionHandler = new Handler() {
77 @Override
78 public void handleMessage(Message msg) {
79 switch (msg.what) {
80 case MSG_TRY_CONNECTING:
81 tryConnecting();
82 break;
83 default:
84 Slog.e(TAG, "Unknown message");
85 }
86 }
87 };
88
89 private ServiceConnection mConnection = new ServiceConnection() {
90 @Override
91 public void onServiceConnected(ComponentName name, IBinder service) {
92 Slog.i(TAG, "MmsService connected");
93 synchronized (MmsServiceBroker.this) {
94 mService = IMms.Stub.asInterface(service);
Ye Wen724dbbd72014-10-07 15:33:51 -070095 MmsServiceBroker.this.notifyAll();
Ye Wend97e1fd2014-07-24 12:56:45 -070096 }
97 }
98
99 @Override
100 public void onServiceDisconnected(ComponentName name) {
101 Slog.i(TAG, "MmsService unexpectedly disconnected");
102 synchronized (MmsServiceBroker.this) {
103 mService = null;
Ye Wen724dbbd72014-10-07 15:33:51 -0700104 MmsServiceBroker.this.notifyAll();
Ye Wend97e1fd2014-07-24 12:56:45 -0700105 }
Ye Wen724dbbd72014-10-07 15:33:51 -0700106 // Retry connecting, but not too eager (with a delay)
107 // since it may come back by itself.
108 mConnectionHandler.sendMessageDelayed(
109 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING),
110 RETRY_DELAY_ON_DISCONNECTION_MS);
Ye Wend97e1fd2014-07-24 12:56:45 -0700111 }
112 };
113
114 public MmsServiceBroker(Context context) {
115 super(context);
116 mContext = context;
117 mService = null;
Ye Wend97e1fd2014-07-24 12:56:45 -0700118 }
119
120 @Override
121 public void onStart() {
122 publishBinderService("imms", new BinderService());
123 }
124
125 public void systemRunning() {
Ye Wenbdc3a462014-11-11 11:17:28 -0800126 Slog.i(TAG, "Delay connecting to MmsService until an API is called");
Ye Wend97e1fd2014-07-24 12:56:45 -0700127 }
128
129 private void tryConnecting() {
130 Slog.i(TAG, "Connecting to MmsService");
131 synchronized (this) {
Ye Wen724dbbd72014-10-07 15:33:51 -0700132 if (mService != null) {
133 Slog.d(TAG, "Already connected");
Ye Wend97e1fd2014-07-24 12:56:45 -0700134 return;
135 }
136 final Intent intent = new Intent();
137 intent.setComponent(MMS_SERVICE_COMPONENT);
138 try {
Ye Wen724dbbd72014-10-07 15:33:51 -0700139 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
140 Slog.e(TAG, "Failed to bind to MmsService");
Ye Wend97e1fd2014-07-24 12:56:45 -0700141 }
142 } catch (SecurityException e) {
Ye Wen724dbbd72014-10-07 15:33:51 -0700143 Slog.e(TAG, "Forbidden to bind to MmsService", e);
Ye Wend97e1fd2014-07-24 12:56:45 -0700144 }
145 }
146 }
147
148 private void ensureService() {
Ye Wen724dbbd72014-10-07 15:33:51 -0700149 synchronized (this) {
150 if (mService == null) {
151 // Service is not connected. Try blocking connecting.
152 Slog.w(TAG, "MmsService not connected. Try connecting...");
153 mConnectionHandler.sendMessage(
154 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
155 final long shouldEnd =
156 SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
157 long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
158 while (waitTime > 0) {
159 try {
160 // TODO: consider using Java concurrent construct instead of raw object wait
161 this.wait(waitTime);
162 } catch (InterruptedException e) {
163 Slog.w(TAG, "Connection wait interrupted", e);
164 }
165 if (mService != null) {
166 // Success
167 return;
168 }
169 // Calculate remaining waiting time to make sure we wait the full timeout period
170 waitTime = shouldEnd - SystemClock.elapsedRealtime();
171 }
172 // Timed out. Something's really wrong.
173 Slog.e(TAG, "Can not connect to MmsService (timed out)");
174 throw new RuntimeException("Timed out in connecting to MmsService");
175 }
Ye Wend97e1fd2014-07-24 12:56:45 -0700176 }
177 }
178
179 /**
180 * Making sure when we obtain the mService instance it is always valid.
181 * Throws {@link RuntimeException} when it is empty.
182 */
183 private IMms getServiceGuarded() {
184 ensureService();
185 return mService;
186 }
187
188 private AppOpsManager getAppOpsManager() {
189 if (mAppOpsManager == null) {
190 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
191 }
192 return mAppOpsManager;
193 }
194
195 private PackageManager getPackageManager() {
196 if (mPackageManager == null) {
197 mPackageManager = mContext.getPackageManager();
198 }
199 return mPackageManager;
200 }
201
202 private TelephonyManager getTelephonyManager() {
203 if (mTelephonyManager == null) {
204 mTelephonyManager = (TelephonyManager) mContext.getSystemService(
205 Context.TELEPHONY_SERVICE);
206 }
207 return mTelephonyManager;
208 }
209
Ye Wenbdc3a462014-11-11 11:17:28 -0800210 private String getCallingPackageName() {
211 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
212 if (packages != null && packages.length > 0) {
213 return packages[0];
214 }
215 return "unknown";
216 }
217
Ye Wend97e1fd2014-07-24 12:56:45 -0700218 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
219 private final class BinderService extends IMms.Stub {
Tom Taylor86201db2014-11-24 09:36:43 -0800220 private static final String PHONE_PACKAGE_NAME = "com.android.phone";
221
Ye Wend97e1fd2014-07-24 12:56:45 -0700222 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700223 public void sendMessage(int subId, String callingPkg, Uri contentUri,
Ye Wen8179c2a2014-09-04 15:36:11 -0700224 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
Julian Odell31ef14d2014-08-25 17:53:52 -0700225 throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800226 Slog.d(TAG, "sendMessage() by " + callingPkg);
Ye Wend97e1fd2014-07-24 12:56:45 -0700227 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
228 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
229 callingPkg) != AppOpsManager.MODE_ALLOWED) {
230 return;
231 }
Tom Taylor86201db2014-11-24 09:36:43 -0800232 contentUri = adjustUriForUserAndGrantPermission(contentUri,
Cheuksan Wang5cec9202014-12-16 13:40:36 -0800233 CarrierMessagingService.SERVICE_INTERFACE,
Tom Taylor86201db2014-11-24 09:36:43 -0800234 Intent.FLAG_GRANT_READ_URI_PERMISSION);
Julian Odell31ef14d2014-08-25 17:53:52 -0700235 getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
236 configOverrides, sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700237 }
238
239 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700240 public void downloadMessage(int subId, String callingPkg, String locationUrl,
Ye Wen8179c2a2014-09-04 15:36:11 -0700241 Uri contentUri, Bundle configOverrides,
Julian Odell31ef14d2014-08-25 17:53:52 -0700242 PendingIntent downloadedIntent) throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800243 Slog.d(TAG, "downloadMessage() by " + callingPkg);
Ye Wend97e1fd2014-07-24 12:56:45 -0700244 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
245 "Download MMS message");
246 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
247 callingPkg) != AppOpsManager.MODE_ALLOWED) {
248 return;
249 }
Tom Taylor86201db2014-11-24 09:36:43 -0800250 contentUri = adjustUriForUserAndGrantPermission(contentUri,
Cheuksan Wang5cec9202014-12-16 13:40:36 -0800251 CarrierMessagingService.SERVICE_INTERFACE,
Tom Taylor86201db2014-11-24 09:36:43 -0800252 Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
253
Julian Odell31ef14d2014-08-25 17:53:52 -0700254 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
255 configOverrides, downloadedIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700256 }
257
258 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700259 public Bundle getCarrierConfigValues(int subId) throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800260 Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
Shri Borde72379722014-09-02 09:48:49 -0700261 return getServiceGuarded().getCarrierConfigValues(subId);
Ye Wend97e1fd2014-07-24 12:56:45 -0700262 }
263
264 @Override
265 public Uri importTextMessage(String callingPkg, String address, int type, String text,
266 long timestampMillis, boolean seen, boolean read) throws RemoteException {
267 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
268 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
269 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700270 // Silently fail AppOps failure due to not being the default SMS app
271 // while writing the TelephonyProvider
272 return FAKE_SMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700273 }
274 return getServiceGuarded().importTextMessage(
275 callingPkg, address, type, text, timestampMillis, seen, read);
276 }
277
278 @Override
Julian Odell31ef14d2014-08-25 17:53:52 -0700279 public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
280 String messageId, long timestampSecs, boolean seen, boolean read)
281 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700282 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
283 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
284 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700285 // Silently fail AppOps failure due to not being the default SMS app
286 // while writing the TelephonyProvider
287 return FAKE_MMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700288 }
289 return getServiceGuarded().importMultimediaMessage(
Julian Odell31ef14d2014-08-25 17:53:52 -0700290 callingPkg, contentUri, messageId, timestampSecs, seen, read);
Ye Wend97e1fd2014-07-24 12:56:45 -0700291 }
292
293 @Override
294 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
295 throws RemoteException {
296 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
297 "Delete SMS/MMS message");
298 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
299 callingPkg) != AppOpsManager.MODE_ALLOWED) {
300 return false;
301 }
302 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
303 }
304
305 @Override
306 public boolean deleteStoredConversation(String callingPkg, long conversationId)
307 throws RemoteException {
308 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
309 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
310 callingPkg) != AppOpsManager.MODE_ALLOWED) {
311 return false;
312 }
313 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
314 }
315
316 @Override
317 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
318 ContentValues statusValues) throws RemoteException {
319 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
320 "Update SMS/MMS message");
321 return getServiceGuarded()
322 .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
323 }
324
325 @Override
Ye Wena3dbd102014-07-29 10:42:25 -0700326 public boolean archiveStoredConversation(String callingPkg, long conversationId,
327 boolean archived) throws RemoteException {
328 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
329 "Update SMS/MMS message");
330 return getServiceGuarded()
331 .archiveStoredConversation(callingPkg, conversationId, archived);
332 }
333
334 @Override
Ye Wend97e1fd2014-07-24 12:56:45 -0700335 public Uri addTextMessageDraft(String callingPkg, String address, String text)
336 throws RemoteException {
337 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
338 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
339 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700340 // Silently fail AppOps failure due to not being the default SMS app
341 // while writing the TelephonyProvider
342 return FAKE_SMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700343 }
344 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
345 }
346
347 @Override
Julian Odell31ef14d2014-08-25 17:53:52 -0700348 public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
349 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700350 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
351 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
352 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700353 // Silently fail AppOps failure due to not being the default SMS app
354 // while writing the TelephonyProvider
355 return FAKE_MMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700356 }
Julian Odell31ef14d2014-08-25 17:53:52 -0700357 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
Ye Wend97e1fd2014-07-24 12:56:45 -0700358 }
359
360 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700361 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
Ye Wen8179c2a2014-09-04 15:36:11 -0700362 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700363 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
364 "Send stored MMS message");
365 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
366 callingPkg) != AppOpsManager.MODE_ALLOWED) {
367 return;
368 }
Ye Wen63c00c42014-08-01 13:38:58 -0700369 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
370 sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700371 }
372
373 @Override
374 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
375 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
376 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
377 callingPkg) != AppOpsManager.MODE_ALLOWED) {
378 return;
379 }
380 getServiceGuarded().setAutoPersisting(callingPkg, enabled);
381 }
382
383 @Override
384 public boolean getAutoPersisting() throws RemoteException {
385 return getServiceGuarded().getAutoPersisting();
386 }
Tom Taylor86201db2014-11-24 09:36:43 -0800387
388 /**
389 * Modifies the Uri to contain the caller's userId, if necessary.
390 * Grants the phone package on primary user permission to access the contentUri,
391 * even if the caller is not in the primary user.
392 *
393 * @param contentUri The Uri to adjust
394 * @param action The intent action used to find the associated carrier app
395 * @param permission The permission to add
396 * @return The adjusted Uri containing the calling userId.
397 */
398 private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
399 int permission) {
400 final int callingUserId = UserHandle.getCallingUserId();
401 if (callingUserId != UserHandle.USER_OWNER) {
402 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
403 }
404 long token = Binder.clearCallingIdentity();
405 try {
406 mContext.grantUriPermission(PHONE_PACKAGE_NAME, contentUri, permission);
407
408 // Grant permission for the carrier app.
409 Intent intent = new Intent(action);
410 TelephonyManager telephonyManager =
411 (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
412 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
413 intent);
414 if (carrierPackages != null && carrierPackages.size() == 1) {
415 mContext.grantUriPermission(carrierPackages.get(0), contentUri, permission);
416 }
417 } finally {
418 Binder.restoreCallingIdentity(token);
419 }
420 return contentUri;
421 }
Ye Wend97e1fd2014-07-24 12:56:45 -0700422 }
423}