blob: 2830b5ed402840aad122304723886138ffcf9d23 [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;
Shri Borde72379722014-09-02 09:48:49 -070032import android.os.Bundle;
Ye Wend97e1fd2014-07-24 12:56:45 -070033import android.os.Handler;
34import android.os.IBinder;
35import android.os.Message;
36import android.os.RemoteException;
Shri Borde72379722014-09-02 09:48:49 -070037import android.os.ServiceManager;
Ye Wend97e1fd2014-07-24 12:56:45 -070038import android.telephony.TelephonyManager;
39import android.util.Slog;
40
41/**
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 Wend97e1fd2014-07-24 12:56:45 -070059 private Context mContext;
60 // The actual MMS service instance to invoke
61 private volatile IMms mService;
62 private boolean mIsConnecting;
63
64 // Cached system service instances
65 private volatile AppOpsManager mAppOpsManager = null;
66 private volatile PackageManager mPackageManager = null;
67 private volatile TelephonyManager mTelephonyManager = null;
68
69 private final Handler mConnectionHandler = new Handler() {
70 @Override
71 public void handleMessage(Message msg) {
72 switch (msg.what) {
73 case MSG_TRY_CONNECTING:
74 tryConnecting();
75 break;
76 default:
77 Slog.e(TAG, "Unknown message");
78 }
79 }
80 };
81
82 private ServiceConnection mConnection = new ServiceConnection() {
83 @Override
84 public void onServiceConnected(ComponentName name, IBinder service) {
85 Slog.i(TAG, "MmsService connected");
86 synchronized (MmsServiceBroker.this) {
87 mService = IMms.Stub.asInterface(service);
88 mIsConnecting = false;
89 }
90 }
91
92 @Override
93 public void onServiceDisconnected(ComponentName name) {
94 Slog.i(TAG, "MmsService unexpectedly disconnected");
95 synchronized (MmsServiceBroker.this) {
96 mService = null;
97 mIsConnecting = false;
98 }
99 }
100 };
101
102 public MmsServiceBroker(Context context) {
103 super(context);
104 mContext = context;
105 mService = null;
106 mIsConnecting = false;
107 }
108
109 @Override
110 public void onStart() {
111 publishBinderService("imms", new BinderService());
112 }
113
114 public void systemRunning() {
115 tryConnecting();
116 }
117
118 private void tryConnecting() {
119 Slog.i(TAG, "Connecting to MmsService");
120 synchronized (this) {
121 if (mIsConnecting) {
122 Slog.d(TAG, "Already connecting");
123 return;
124 }
125 final Intent intent = new Intent();
126 intent.setComponent(MMS_SERVICE_COMPONENT);
127 try {
128 if (mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
129 mIsConnecting = true;
130 } else {
131 Slog.e(TAG, "Failed to connect to MmsService");
132 }
133 } catch (SecurityException e) {
134 Slog.e(TAG, "Forbidden to connect to MmsService", e);
135 }
136 }
137 }
138
139 private void ensureService() {
140 if (mService == null) {
141 // Service instance lost, kicking off the connection again
142 mConnectionHandler.sendMessage(mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
143 throw new RuntimeException("MMS service is not connected");
144 }
145 }
146
147 /**
148 * Making sure when we obtain the mService instance it is always valid.
149 * Throws {@link RuntimeException} when it is empty.
150 */
151 private IMms getServiceGuarded() {
152 ensureService();
153 return mService;
154 }
155
156 private AppOpsManager getAppOpsManager() {
157 if (mAppOpsManager == null) {
158 mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
159 }
160 return mAppOpsManager;
161 }
162
163 private PackageManager getPackageManager() {
164 if (mPackageManager == null) {
165 mPackageManager = mContext.getPackageManager();
166 }
167 return mPackageManager;
168 }
169
170 private TelephonyManager getTelephonyManager() {
171 if (mTelephonyManager == null) {
172 mTelephonyManager = (TelephonyManager) mContext.getSystemService(
173 Context.TELEPHONY_SERVICE);
174 }
175 return mTelephonyManager;
176 }
177
178 /*
179 * Throws a security exception unless the caller has carrier privilege.
180 */
181 private void enforceCarrierPrivilege() {
182 String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
183 for (String pkg : packages) {
184 if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
185 TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
186 return;
187 }
188 }
189 throw new SecurityException("No carrier privilege");
190 }
191
192 // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
193 private final class BinderService extends IMms.Stub {
194 @Override
195 public void sendMessage(long subId, String callingPkg, byte[] pdu, String locationUrl,
Ye Wen63c00c42014-08-01 13:38:58 -0700196 ContentValues configOverrides, PendingIntent sentIntent) throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700197 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
198 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
199 callingPkg) != AppOpsManager.MODE_ALLOWED) {
200 return;
201 }
Ye Wen63c00c42014-08-01 13:38:58 -0700202 getServiceGuarded().sendMessage(subId, callingPkg, pdu, locationUrl, configOverrides,
203 sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700204 }
205
206 @Override
207 public void downloadMessage(long subId, String callingPkg, String locationUrl,
Ye Wen63c00c42014-08-01 13:38:58 -0700208 ContentValues configOverrides, PendingIntent downloadedIntent)
209 throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700210 mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
211 "Download MMS message");
212 if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
213 callingPkg) != AppOpsManager.MODE_ALLOWED) {
214 return;
215 }
Ye Wen63c00c42014-08-01 13:38:58 -0700216 getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, configOverrides,
217 downloadedIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700218 }
219
220 @Override
221 public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException {
222 enforceCarrierPrivilege();
223 getServiceGuarded().updateMmsSendStatus(messageRef, success);
224 }
225
226 @Override
227 public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException {
228 enforceCarrierPrivilege();
229 getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu);
230 }
231
232 @Override
Shri Borde72379722014-09-02 09:48:49 -0700233 public Bundle getCarrierConfigValues(long subId) throws RemoteException {
234 return getServiceGuarded().getCarrierConfigValues(subId);
Ye Wend97e1fd2014-07-24 12:56:45 -0700235 }
236
237 @Override
238 public Uri importTextMessage(String callingPkg, String address, int type, String text,
239 long timestampMillis, boolean seen, boolean read) throws RemoteException {
240 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
241 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
242 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700243 // Silently fail AppOps failure due to not being the default SMS app
244 // while writing the TelephonyProvider
245 return FAKE_SMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700246 }
247 return getServiceGuarded().importTextMessage(
248 callingPkg, address, type, text, timestampMillis, seen, read);
249 }
250
251 @Override
252 public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId,
253 long timestampSecs, boolean seen, boolean read) throws RemoteException {
254 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
255 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
256 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700257 // Silently fail AppOps failure due to not being the default SMS app
258 // while writing the TelephonyProvider
259 return FAKE_MMS_SENT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700260 }
261 return getServiceGuarded().importMultimediaMessage(
262 callingPkg, pdu, messageId, timestampSecs, seen, read);
263 }
264
265 @Override
266 public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
267 throws RemoteException {
268 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
269 "Delete SMS/MMS message");
270 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
271 callingPkg) != AppOpsManager.MODE_ALLOWED) {
272 return false;
273 }
274 return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
275 }
276
277 @Override
278 public boolean deleteStoredConversation(String callingPkg, long conversationId)
279 throws RemoteException {
280 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
281 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
282 callingPkg) != AppOpsManager.MODE_ALLOWED) {
283 return false;
284 }
285 return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
286 }
287
288 @Override
289 public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
290 ContentValues statusValues) throws RemoteException {
291 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
292 "Update SMS/MMS message");
293 return getServiceGuarded()
294 .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
295 }
296
297 @Override
Ye Wena3dbd102014-07-29 10:42:25 -0700298 public boolean archiveStoredConversation(String callingPkg, long conversationId,
299 boolean archived) throws RemoteException {
300 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
301 "Update SMS/MMS message");
302 return getServiceGuarded()
303 .archiveStoredConversation(callingPkg, conversationId, archived);
304 }
305
306 @Override
Ye Wend97e1fd2014-07-24 12:56:45 -0700307 public Uri addTextMessageDraft(String callingPkg, String address, String text)
308 throws RemoteException {
309 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
310 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
311 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700312 // Silently fail AppOps failure due to not being the default SMS app
313 // while writing the TelephonyProvider
314 return FAKE_SMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700315 }
316 return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
317 }
318
319 @Override
320 public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException {
321 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
322 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
323 callingPkg) != AppOpsManager.MODE_ALLOWED) {
Ye Wenfa58ac02014-07-31 17:15:30 -0700324 // Silently fail AppOps failure due to not being the default SMS app
325 // while writing the TelephonyProvider
326 return FAKE_MMS_DRAFT_URI;
Ye Wend97e1fd2014-07-24 12:56:45 -0700327 }
328 return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
329 }
330
331 @Override
332 public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
Ye Wen63c00c42014-08-01 13:38:58 -0700333 ContentValues configOverrides, PendingIntent sentIntent) throws RemoteException {
Ye Wend97e1fd2014-07-24 12:56:45 -0700334 mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
335 "Send stored MMS message");
336 if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
337 callingPkg) != AppOpsManager.MODE_ALLOWED) {
338 return;
339 }
Ye Wen63c00c42014-08-01 13:38:58 -0700340 getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
341 sentIntent);
Ye Wend97e1fd2014-07-24 12:56:45 -0700342 }
343
344 @Override
345 public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
346 mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
347 if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
348 callingPkg) != AppOpsManager.MODE_ALLOWED) {
349 return;
350 }
351 getServiceGuarded().setAutoPersisting(callingPkg, enabled);
352 }
353
354 @Override
355 public boolean getAutoPersisting() throws RemoteException {
356 return getServiceGuarded().getAutoPersisting();
357 }
358 }
359}