blob: 898b6f1106abd2d2c969d4b72778a44f570d1a47 [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
19import com.android.internal.telephony.IMms;
20
21import android.Manifest;
22import android.app.AppOpsManager;
23import android.app.PendingIntent;
24import android.content.ComponentName;
25import android.content.ContentValues;
26import android.content.Context;
27import android.content.Intent;
28import android.content.ServiceConnection;
29import android.content.pm.PackageManager;
30import android.net.Uri;
31import android.os.Binder;
32import android.os.Handler;
33import android.os.IBinder;
34import android.os.Message;
35import android.os.RemoteException;
36import android.telephony.TelephonyManager;
37import android.util.Slog;
38
39/**
40 * This class is a proxy for MmsService APIs. We need this because MmsService runs
41 * in phone process and may crash anytime. This manages a connection to the actual
42 * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
43 */
44public class MmsServiceBroker extends SystemService {
45 private static final String TAG = "MmsServiceBroker";
46
47 private static final ComponentName MMS_SERVICE_COMPONENT =
48 new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
49
50 private static final int MSG_TRY_CONNECTING = 1;
51
Ye Wenfa58ac02014-07-31 17:15:30 -070052 private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
53 private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
54 private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
55 private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
56
Ye Wend97e1fd2014-07-24 12:56:45 -070057 private Context mContext;
58 // The actual MMS service instance to invoke
59 private volatile IMms mService;
60 private boolean mIsConnecting;
61
62 // Cached system service instances
63 private volatile AppOpsManager mAppOpsManager = null;
64 private volatile PackageManager mPackageManager = null;
65 private volatile TelephonyManager mTelephonyManager = null;
66
67 private final Handler mConnectionHandler = new Handler() {
68 @Override
69 public void handleMessage(Message msg) {
70 switch (msg.what) {
71 case MSG_TRY_CONNECTING:
72 tryConnecting();
73 break;
74 default:
75 Slog.e(TAG, "Unknown message");
76 }
77 }
78 };
79
80 private ServiceConnection mConnection = new ServiceConnection() {
81 @Override
82 public void onServiceConnected(ComponentName name, IBinder service) {
83 Slog.i(TAG, "MmsService connected");
84 synchronized (MmsServiceBroker.this) {
85 mService = IMms.Stub.asInterface(service);
86 mIsConnecting = false;
87 }
88 }
89
90 @Override
91 public void onServiceDisconnected(ComponentName name) {
92 Slog.i(TAG, "MmsService unexpectedly disconnected");
93 synchronized (MmsServiceBroker.this) {
94 mService = null;
95 mIsConnecting = false;
96 }
97 }
98 };
99
100 public MmsServiceBroker(Context context) {
101 super(context);
102 mContext = context;
103 mService = null;
104 mIsConnecting = false;
105 }
106
107 @Override
108 public void onStart() {
109 publishBinderService("imms", new BinderService());
110 }
111
112 public void systemRunning() {
113 tryConnecting();
114 }
115
116 private void tryConnecting() {
117 Slog.i(TAG, "Connecting to MmsService");
118 synchronized (this) {
119 if (mIsConnecting) {
120 Slog.d(TAG, "Already connecting");
121 return;
122 }
123 final Intent intent = new Intent();
124 intent.setComponent(MMS_SERVICE_COMPONENT);
125 try {
126 if (mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
127 mIsConnecting = true;
128 } else {
129 Slog.e(TAG, "Failed to connect to MmsService");
130 }
131 } catch (SecurityException e) {
132 Slog.e(TAG, "Forbidden to connect to MmsService", e);
133 }
134 }
135 }
136
137 private void ensureService() {
138 if (mService == null) {
139 // Service instance lost, kicking off the connection again
140 mConnectionHandler.sendMessage(mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
141 throw new RuntimeException("MMS service is not connected");
142 }
143 }
144
145 /**
146 * Making sure when we obtain the mService instance it is always valid.
147 * Throws {@link RuntimeException} when it is empty.
148 */
149 private IMms getServiceGuarded() {
150 ensureService();
151 return mService;
152 }
153
154 private AppOpsManager getAppOpsManager() {
155 if (mAppOpsManager == null) {
156 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
157 }
158 return mAppOpsManager;
159 }
160
161 private PackageManager getPackageManager() {
162 if (mPackageManager == null) {
163 mPackageManager = mContext.getPackageManager();
164 }
165 return mPackageManager;
166 }
167
168 private TelephonyManager getTelephonyManager() {
169 if (mTelephonyManager == null) {
170 mTelephonyManager = (TelephonyManager) mContext.getSystemService(
171 Context.TELEPHONY_SERVICE);
172 }
173 return mTelephonyManager;
174 }
175
176 /*
177 * Throws a security exception unless the caller has carrier privilege.
178 */
179 private void enforceCarrierPrivilege() {
180 String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
181 for (String pkg : packages) {
182 if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
183 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
184 return;
185 }
186 }
187 throw new SecurityException("No carrier privilege");
188 }
189
190 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
191 private final class BinderService extends IMms.Stub {
192 @Override
193 public void sendMessage(long subId, String callingPkg, byte[] pdu, String locationUrl,
194 PendingIntent sentIntent) throws RemoteException {
195 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
196 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
197 callingPkg) != AppOpsManager.MODE_ALLOWED) {
198 return;
199 }
200 getServiceGuarded().sendMessage(subId, callingPkg, pdu, locationUrl, sentIntent);
201 }
202
203 @Override
204 public void downloadMessage(long subId, String callingPkg, String locationUrl,
205 PendingIntent downloadedIntent) throws RemoteException {
206 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
207 "Download MMS message");
208 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
209 callingPkg) != AppOpsManager.MODE_ALLOWED) {
210 return;
211 }
212 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, downloadedIntent);
213 }
214
215 @Override
216 public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException {
217 enforceCarrierPrivilege();
218 getServiceGuarded().updateMmsSendStatus(messageRef, success);
219 }
220
221 @Override
222 public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException {
223 enforceCarrierPrivilege();
224 getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu);
225 }
226
227 @Override
228 public boolean getCarrierConfigBoolean(String name, boolean defaultValue)
229 throws RemoteException {
230 return getServiceGuarded().getCarrierConfigBoolean(name, defaultValue);
231 }
232
233 @Override
234 public int getCarrierConfigInt(String name, int defaultValue) throws RemoteException {
235 return getServiceGuarded().getCarrierConfigInt(name, defaultValue);
236 }
237
238 @Override
239 public String getCarrierConfigString(String name, String defaultValue)
240 throws RemoteException {
241 return getServiceGuarded().getCarrierConfigString(name, defaultValue);
242 }
243
244 @Override
245 public void setCarrierConfigBoolean(String callingPkg, String name, boolean value)
246 throws RemoteException {
247 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
248 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
249 callingPkg) != AppOpsManager.MODE_ALLOWED) {
250 return;
251 }
252 getServiceGuarded().setCarrierConfigBoolean(callingPkg, name, value);
253 }
254
255 @Override
256 public void setCarrierConfigInt(String callingPkg, String name, int value)
257 throws RemoteException {
258 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
259 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
260 callingPkg) != AppOpsManager.MODE_ALLOWED) {
261 return;
262 }
263 getServiceGuarded().setCarrierConfigInt(callingPkg, name, value);
264 }
265
266 @Override
267 public void setCarrierConfigString(String callingPkg, String name, String value)
268 throws RemoteException {
269 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
270 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
271 callingPkg) != AppOpsManager.MODE_ALLOWED) {
272 return;
273 }
274 getServiceGuarded().setCarrierConfigString(callingPkg, name, value);
275 }
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
292 public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId,
293 long timestampSecs, boolean seen, boolean read) throws RemoteException {
294 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
295 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
296 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700297 // Silently fail AppOps failure due to not being the default SMS app
298 // while writing the TelephonyProvider
299 return FAKE_MMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700300 }
301 return getServiceGuarded().importMultimediaMessage(
302 callingPkg, pdu, messageId, timestampSecs, seen, read);
303 }
304
305 @Override
306 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
307 throws RemoteException {
308 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
309 "Delete SMS/MMS message");
310 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
311 callingPkg) != AppOpsManager.MODE_ALLOWED) {
312 return false;
313 }
314 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
315 }
316
317 @Override
318 public boolean deleteStoredConversation(String callingPkg, long conversationId)
319 throws RemoteException {
320 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
321 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
322 callingPkg) != AppOpsManager.MODE_ALLOWED) {
323 return false;
324 }
325 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
326 }
327
328 @Override
329 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
330 ContentValues statusValues) throws RemoteException {
331 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
332 "Update SMS/MMS message");
333 return getServiceGuarded()
334 .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
335 }
336
337 @Override
Ye Wena3dbd102014-07-29 10:42:25 -0700338 public boolean archiveStoredConversation(String callingPkg, long conversationId,
339 boolean archived) throws RemoteException {
340 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
341 "Update SMS/MMS message");
342 return getServiceGuarded()
343 .archiveStoredConversation(callingPkg, conversationId, archived);
344 }
345
346 @Override
Ye Wend97e1fd2014-07-24 12:56:45 -0700347 public Uri addTextMessageDraft(String callingPkg, String address, String text)
348 throws RemoteException {
349 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
350 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
351 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700352 // Silently fail AppOps failure due to not being the default SMS app
353 // while writing the TelephonyProvider
354 return FAKE_SMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700355 }
356 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
357 }
358
359 @Override
360 public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException {
361 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
362 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
363 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700364 // Silently fail AppOps failure due to not being the default SMS app
365 // while writing the TelephonyProvider
366 return FAKE_MMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700367 }
368 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
369 }
370
371 @Override
372 public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
373 PendingIntent sentIntent) throws RemoteException {
374 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
375 "Send stored MMS message");
376 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
377 callingPkg) != AppOpsManager.MODE_ALLOWED) {
378 return;
379 }
380 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, sentIntent);
381 }
382
383 @Override
384 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
385 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
386 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
387 callingPkg) != AppOpsManager.MODE_ALLOWED) {
388 return;
389 }
390 getServiceGuarded().setAutoPersisting(callingPkg, enabled);
391 }
392
393 @Override
394 public boolean getAutoPersisting() throws RemoteException {
395 return getServiceGuarded().getAutoPersisting();
396 }
397 }
398}