blob: da507519676e43b02fa1f15cfaa4955c05df8f63 [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;
23import android.content.ContentValues;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.content.pm.PackageManager;
28import android.net.Uri;
29import android.os.Binder;
Shri Borde72379722014-09-02 09:48:49 -070030import android.os.Bundle;
Ye Wend97e1fd2014-07-24 12:56:45 -070031import android.os.Handler;
32import android.os.IBinder;
33import android.os.Message;
34import android.os.RemoteException;
Ye Wen724dbbd72014-10-07 15:33:51 -070035import android.os.SystemClock;
Ye Wend97e1fd2014-07-24 12:56:45 -070036import android.telephony.TelephonyManager;
37import android.util.Slog;
38
Ye Wenbdc3a462014-11-11 11:17:28 -080039import com.android.internal.telephony.IMms;
40
Ye Wend97e1fd2014-07-24 12:56:45 -070041/**
42 * This class is a proxy for MmsService APIs. We need this because MmsService runs
43 * in phone process and may crash anytime. This manages a connection to the actual
44 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
45 */
46public class MmsServiceBroker extends SystemService {
47 private static final String TAG = "MmsServiceBroker";
48
49 private static final ComponentName MMS_SERVICE_COMPONENT =
50 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
51
52 private static final int MSG_TRY_CONNECTING = 1;
53
Ye Wenfa58ac02014-07-31 17:15:30 -070054 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
55 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
56 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
57 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
58
Ye Wen724dbbd72014-10-07 15:33:51 -070059 private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
60 private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds
61
Ye Wend97e1fd2014-07-24 12:56:45 -070062 private Context mContext;
63 // The actual MMS service instance to invoke
64 private volatile IMms mService;
Ye Wend97e1fd2014-07-24 12:56:45 -070065
66 // Cached system service instances
67 private volatile AppOpsManager mAppOpsManager = null;
68 private volatile PackageManager mPackageManager = null;
69 private volatile TelephonyManager mTelephonyManager = null;
70
71 private final Handler mConnectionHandler = new Handler() {
72 @Override
73 public void handleMessage(Message msg) {
74 switch (msg.what) {
75 case MSG_TRY_CONNECTING:
76 tryConnecting();
77 break;
78 default:
79 Slog.e(TAG, "Unknown message");
80 }
81 }
82 };
83
84 private ServiceConnection mConnection = new ServiceConnection() {
85 @Override
86 public void onServiceConnected(ComponentName name, IBinder service) {
87 Slog.i(TAG, "MmsService connected");
88 synchronized (MmsServiceBroker.this) {
89 mService = IMms.Stub.asInterface(service);
Ye Wen724dbbd72014-10-07 15:33:51 -070090 MmsServiceBroker.this.notifyAll();
Ye Wend97e1fd2014-07-24 12:56:45 -070091 }
92 }
93
94 @Override
95 public void onServiceDisconnected(ComponentName name) {
96 Slog.i(TAG, "MmsService unexpectedly disconnected");
97 synchronized (MmsServiceBroker.this) {
98 mService = null;
Ye Wen724dbbd72014-10-07 15:33:51 -070099 MmsServiceBroker.this.notifyAll();
Ye Wend97e1fd2014-07-24 12:56:45 -0700100 }
Ye Wen724dbbd72014-10-07 15:33:51 -0700101 // Retry connecting, but not too eager (with a delay)
102 // since it may come back by itself.
103 mConnectionHandler.sendMessageDelayed(
104 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING),
105 RETRY_DELAY_ON_DISCONNECTION_MS);
Ye Wend97e1fd2014-07-24 12:56:45 -0700106 }
107 };
108
109 public MmsServiceBroker(Context context) {
110 super(context);
111 mContext = context;
112 mService = null;
Ye Wend97e1fd2014-07-24 12:56:45 -0700113 }
114
115 @Override
116 public void onStart() {
117 publishBinderService("imms", new BinderService());
118 }
119
120 public void systemRunning() {
Ye Wenbdc3a462014-11-11 11:17:28 -0800121 Slog.i(TAG, "Delay connecting to MmsService until an API is called");
Ye Wend97e1fd2014-07-24 12:56:45 -0700122 }
123
124 private void tryConnecting() {
125 Slog.i(TAG, "Connecting to MmsService");
126 synchronized (this) {
Ye Wen724dbbd72014-10-07 15:33:51 -0700127 if (mService != null) {
128 Slog.d(TAG, "Already connected");
Ye Wend97e1fd2014-07-24 12:56:45 -0700129 return;
130 }
131 final Intent intent = new Intent();
132 intent.setComponent(MMS_SERVICE_COMPONENT);
133 try {
Ye Wen724dbbd72014-10-07 15:33:51 -0700134 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
135 Slog.e(TAG, "Failed to bind to MmsService");
Ye Wend97e1fd2014-07-24 12:56:45 -0700136 }
137 } catch (SecurityException e) {
Ye Wen724dbbd72014-10-07 15:33:51 -0700138 Slog.e(TAG, "Forbidden to bind to MmsService", e);
Ye Wend97e1fd2014-07-24 12:56:45 -0700139 }
140 }
141 }
142
143 private void ensureService() {
Ye Wen724dbbd72014-10-07 15:33:51 -0700144 synchronized (this) {
145 if (mService == null) {
146 // Service is not connected. Try blocking connecting.
147 Slog.w(TAG, "MmsService not connected. Try connecting...");
148 mConnectionHandler.sendMessage(
149 mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
150 final long shouldEnd =
151 SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
152 long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
153 while (waitTime > 0) {
154 try {
155 // TODO: consider using Java concurrent construct instead of raw object wait
156 this.wait(waitTime);
157 } catch (InterruptedException e) {
158 Slog.w(TAG, "Connection wait interrupted", e);
159 }
160 if (mService != null) {
161 // Success
162 return;
163 }
164 // Calculate remaining waiting time to make sure we wait the full timeout period
165 waitTime = shouldEnd - SystemClock.elapsedRealtime();
166 }
167 // Timed out. Something's really wrong.
168 Slog.e(TAG, "Can not connect to MmsService (timed out)");
169 throw new RuntimeException("Timed out in connecting to MmsService");
170 }
Ye Wend97e1fd2014-07-24 12:56:45 -0700171 }
172 }
173
174 /**
175 * Making sure when we obtain the mService instance it is always valid.
176 * Throws {@link RuntimeException} when it is empty.
177 */
178 private IMms getServiceGuarded() {
179 ensureService();
180 return mService;
181 }
182
183 private AppOpsManager getAppOpsManager() {
184 if (mAppOpsManager == null) {
185 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
186 }
187 return mAppOpsManager;
188 }
189
190 private PackageManager getPackageManager() {
191 if (mPackageManager == null) {
192 mPackageManager = mContext.getPackageManager();
193 }
194 return mPackageManager;
195 }
196
197 private TelephonyManager getTelephonyManager() {
198 if (mTelephonyManager == null) {
199 mTelephonyManager = (TelephonyManager) mContext.getSystemService(
200 Context.TELEPHONY_SERVICE);
201 }
202 return mTelephonyManager;
203 }
204
205 /*
206 * Throws a security exception unless the caller has carrier privilege.
207 */
208 private void enforceCarrierPrivilege() {
Ye Wenbdc3a462014-11-11 11:17:28 -0800209 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
Ye Wend97e1fd2014-07-24 12:56:45 -0700210 for (String pkg : packages) {
211 if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
212 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
213 return;
214 }
215 }
216 throw new SecurityException("No carrier privilege");
217 }
218
Ye Wenbdc3a462014-11-11 11:17:28 -0800219 private String getCallingPackageName() {
220 final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
221 if (packages != null && packages.length > 0) {
222 return packages[0];
223 }
224 return "unknown";
225 }
226
Ye Wend97e1fd2014-07-24 12:56:45 -0700227 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
228 private final class BinderService extends IMms.Stub {
229 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700230 public void sendMessage(int subId, String callingPkg, Uri contentUri,
Ye Wen8179c2a2014-09-04 15:36:11 -0700231 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
Julian Odell31ef14d2014-08-25 17:53:52 -0700232 throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800233 Slog.d(TAG, "sendMessage() by " + callingPkg);
Ye Wend97e1fd2014-07-24 12:56:45 -0700234 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
235 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
236 callingPkg) != AppOpsManager.MODE_ALLOWED) {
237 return;
238 }
Julian Odell31ef14d2014-08-25 17:53:52 -0700239 getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
240 configOverrides, sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700241 }
242
243 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700244 public void downloadMessage(int subId, String callingPkg, String locationUrl,
Ye Wen8179c2a2014-09-04 15:36:11 -0700245 Uri contentUri, Bundle configOverrides,
Julian Odell31ef14d2014-08-25 17:53:52 -0700246 PendingIntent downloadedIntent) throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800247 Slog.d(TAG, "downloadMessage() by " + callingPkg);
Ye Wend97e1fd2014-07-24 12:56:45 -0700248 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
249 "Download MMS message");
250 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
251 callingPkg) != AppOpsManager.MODE_ALLOWED) {
252 return;
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
Cheuksan Wangee045432014-09-08 19:54:58 -0700259 public void updateMmsSendStatus(int messageRef, byte[] pdu, int status)
260 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700261 enforceCarrierPrivilege();
Cheuksan Wangee045432014-09-08 19:54:58 -0700262 getServiceGuarded().updateMmsSendStatus(messageRef, pdu, status);
Ye Wend97e1fd2014-07-24 12:56:45 -0700263 }
264
265 @Override
Cheuksan Wangee045432014-09-08 19:54:58 -0700266 public void updateMmsDownloadStatus(int messageRef, int status) throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700267 enforceCarrierPrivilege();
Cheuksan Wangee045432014-09-08 19:54:58 -0700268 getServiceGuarded().updateMmsDownloadStatus(messageRef, status);
Ye Wend97e1fd2014-07-24 12:56:45 -0700269 }
270
271 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700272 public Bundle getCarrierConfigValues(int subId) throws RemoteException {
Ye Wenbdc3a462014-11-11 11:17:28 -0800273 Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
Shri Borde72379722014-09-02 09:48:49 -0700274 return getServiceGuarded().getCarrierConfigValues(subId);
Ye Wend97e1fd2014-07-24 12:56:45 -0700275 }
276
277 @Override
278 public Uri importTextMessage(String callingPkg, String address, int type, String text,
279 long timestampMillis, boolean seen, boolean read) throws RemoteException {
280 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
281 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
282 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700283 // Silently fail AppOps failure due to not being the default SMS app
284 // while writing the TelephonyProvider
285 return FAKE_SMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700286 }
287 return getServiceGuarded().importTextMessage(
288 callingPkg, address, type, text, timestampMillis, seen, read);
289 }
290
291 @Override
Julian Odell31ef14d2014-08-25 17:53:52 -0700292 public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
293 String messageId, long timestampSecs, boolean seen, boolean read)
294 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700295 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
296 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
297 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700298 // Silently fail AppOps failure due to not being the default SMS app
299 // while writing the TelephonyProvider
300 return FAKE_MMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700301 }
302 return getServiceGuarded().importMultimediaMessage(
Julian Odell31ef14d2014-08-25 17:53:52 -0700303 callingPkg, contentUri, messageId, timestampSecs, seen, read);
Ye Wend97e1fd2014-07-24 12:56:45 -0700304 }
305
306 @Override
307 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
308 throws RemoteException {
309 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
310 "Delete SMS/MMS message");
311 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
312 callingPkg) != AppOpsManager.MODE_ALLOWED) {
313 return false;
314 }
315 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
316 }
317
318 @Override
319 public boolean deleteStoredConversation(String callingPkg, long conversationId)
320 throws RemoteException {
321 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
322 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
323 callingPkg) != AppOpsManager.MODE_ALLOWED) {
324 return false;
325 }
326 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
327 }
328
329 @Override
330 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
331 ContentValues statusValues) throws RemoteException {
332 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
333 "Update SMS/MMS message");
334 return getServiceGuarded()
335 .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
336 }
337
338 @Override
Ye Wena3dbd102014-07-29 10:42:25 -0700339 public boolean archiveStoredConversation(String callingPkg, long conversationId,
340 boolean archived) throws RemoteException {
341 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
342 "Update SMS/MMS message");
343 return getServiceGuarded()
344 .archiveStoredConversation(callingPkg, conversationId, archived);
345 }
346
347 @Override
Ye Wend97e1fd2014-07-24 12:56:45 -0700348 public Uri addTextMessageDraft(String callingPkg, String address, String text)
349 throws RemoteException {
350 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS 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_SMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700356 }
357 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
358 }
359
360 @Override
Julian Odell31ef14d2014-08-25 17:53:52 -0700361 public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
362 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700363 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
364 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
365 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700366 // Silently fail AppOps failure due to not being the default SMS app
367 // while writing the TelephonyProvider
368 return FAKE_MMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700369 }
Julian Odell31ef14d2014-08-25 17:53:52 -0700370 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
Ye Wend97e1fd2014-07-24 12:56:45 -0700371 }
372
373 @Override
Wink Saville63f03dd2014-10-23 10:44:45 -0700374 public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
Ye Wen8179c2a2014-09-04 15:36:11 -0700375 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700376 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
377 "Send stored MMS message");
378 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
379 callingPkg) != AppOpsManager.MODE_ALLOWED) {
380 return;
381 }
Ye Wen63c00c42014-08-01 13:38:58 -0700382 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
383 sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700384 }
385
386 @Override
387 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
388 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
389 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
390 callingPkg) != AppOpsManager.MODE_ALLOWED) {
391 return;
392 }
393 getServiceGuarded().setAutoPersisting(callingPkg, enabled);
394 }
395
396 @Override
397 public boolean getAutoPersisting() throws RemoteException {
398 return getServiceGuarded().getAutoPersisting();
399 }
400 }
401}