MmsServiceBroker.java revision a3dbd1087dfad0cacdb274420ec70505e236ad42
1/*
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
52    private Context mContext;
53    // The actual MMS service instance to invoke
54    private volatile IMms mService;
55    private boolean mIsConnecting;
56
57    // Cached system service instances
58    private volatile AppOpsManager mAppOpsManager = null;
59    private volatile PackageManager mPackageManager = null;
60    private volatile TelephonyManager mTelephonyManager = null;
61
62    private final Handler mConnectionHandler = new Handler() {
63        @Override
64        public void handleMessage(Message msg) {
65            switch (msg.what) {
66                case MSG_TRY_CONNECTING:
67                    tryConnecting();
68                    break;
69                default:
70                    Slog.e(TAG, "Unknown message");
71            }
72        }
73    };
74
75    private ServiceConnection mConnection = new ServiceConnection() {
76        @Override
77        public void onServiceConnected(ComponentName name, IBinder service) {
78            Slog.i(TAG, "MmsService connected");
79            synchronized (MmsServiceBroker.this) {
80                mService = IMms.Stub.asInterface(service);
81                mIsConnecting = false;
82            }
83        }
84
85        @Override
86        public void onServiceDisconnected(ComponentName name) {
87            Slog.i(TAG, "MmsService unexpectedly disconnected");
88            synchronized (MmsServiceBroker.this) {
89                mService = null;
90                mIsConnecting = false;
91            }
92        }
93    };
94
95    public MmsServiceBroker(Context context) {
96        super(context);
97        mContext = context;
98        mService = null;
99        mIsConnecting = false;
100    }
101
102    @Override
103    public void onStart() {
104        publishBinderService("imms", new BinderService());
105    }
106
107    public void systemRunning() {
108        tryConnecting();
109    }
110
111    private void tryConnecting() {
112        Slog.i(TAG, "Connecting to MmsService");
113        synchronized (this) {
114            if (mIsConnecting) {
115                Slog.d(TAG, "Already connecting");
116                return;
117            }
118            final Intent intent = new Intent();
119            intent.setComponent(MMS_SERVICE_COMPONENT);
120            try {
121                if (mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
122                    mIsConnecting = true;
123                } else {
124                    Slog.e(TAG, "Failed to connect to MmsService");
125                }
126            } catch (SecurityException e) {
127                Slog.e(TAG, "Forbidden to connect to MmsService", e);
128            }
129        }
130    }
131
132    private void ensureService() {
133        if (mService == null) {
134            // Service instance lost, kicking off the connection again
135            mConnectionHandler.sendMessage(mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
136            throw new RuntimeException("MMS service is not connected");
137        }
138    }
139
140    /**
141     * Making sure when we obtain the mService instance it is always valid.
142     * Throws {@link RuntimeException} when it is empty.
143     */
144    private IMms getServiceGuarded() {
145        ensureService();
146        return mService;
147    }
148
149    private AppOpsManager getAppOpsManager() {
150        if (mAppOpsManager == null) {
151            mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
152        }
153        return mAppOpsManager;
154    }
155
156    private PackageManager getPackageManager() {
157        if (mPackageManager == null) {
158            mPackageManager = mContext.getPackageManager();
159        }
160        return mPackageManager;
161    }
162
163    private TelephonyManager getTelephonyManager() {
164        if (mTelephonyManager == null) {
165            mTelephonyManager = (TelephonyManager) mContext.getSystemService(
166                    Context.TELEPHONY_SERVICE);
167        }
168        return mTelephonyManager;
169    }
170
171    /*
172     * Throws a security exception unless the caller has carrier privilege.
173     */
174    private void enforceCarrierPrivilege() {
175        String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
176        for (String pkg : packages) {
177            if (getTelephonyManager().checkCarrierPrivilegesForPackage(pkg) ==
178                    TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
179                return;
180            }
181        }
182        throw new SecurityException("No carrier privilege");
183    }
184
185    // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
186    private final class BinderService extends IMms.Stub {
187        @Override
188        public void sendMessage(long subId, String callingPkg, byte[] pdu, String locationUrl,
189                PendingIntent sentIntent) throws RemoteException {
190            mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
191            if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
192                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
193                return;
194            }
195            getServiceGuarded().sendMessage(subId, callingPkg, pdu, locationUrl, sentIntent);
196        }
197
198        @Override
199        public void downloadMessage(long subId, String callingPkg, String locationUrl,
200                PendingIntent downloadedIntent) throws RemoteException {
201            mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
202                    "Download MMS message");
203            if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
204                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
205                return;
206            }
207            getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, downloadedIntent);
208        }
209
210        @Override
211        public void updateMmsSendStatus(int messageRef, boolean success) throws RemoteException {
212            enforceCarrierPrivilege();
213            getServiceGuarded().updateMmsSendStatus(messageRef, success);
214        }
215
216        @Override
217        public void updateMmsDownloadStatus(int messageRef, byte[] pdu) throws RemoteException {
218            enforceCarrierPrivilege();
219            getServiceGuarded().updateMmsDownloadStatus(messageRef, pdu);
220        }
221
222        @Override
223        public boolean getCarrierConfigBoolean(String name, boolean defaultValue)
224                throws RemoteException {
225            return getServiceGuarded().getCarrierConfigBoolean(name, defaultValue);
226        }
227
228        @Override
229        public int getCarrierConfigInt(String name, int defaultValue) throws RemoteException {
230            return getServiceGuarded().getCarrierConfigInt(name, defaultValue);
231        }
232
233        @Override
234        public String getCarrierConfigString(String name, String defaultValue)
235                throws RemoteException {
236            return getServiceGuarded().getCarrierConfigString(name, defaultValue);
237        }
238
239        @Override
240        public void setCarrierConfigBoolean(String callingPkg, String name, boolean value)
241                throws RemoteException {
242            mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
243            if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
244                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
245                return;
246            }
247            getServiceGuarded().setCarrierConfigBoolean(callingPkg, name, value);
248        }
249
250        @Override
251        public void setCarrierConfigInt(String callingPkg, String name, int value)
252                throws RemoteException {
253            mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
254            if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
255                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
256                return;
257            }
258            getServiceGuarded().setCarrierConfigInt(callingPkg, name, value);
259        }
260
261        @Override
262        public void setCarrierConfigString(String callingPkg, String name, String value)
263                throws RemoteException {
264            mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Set MMS config");
265            if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
266                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
267                return;
268            }
269            getServiceGuarded().setCarrierConfigString(callingPkg, name, value);
270        }
271
272        @Override
273        public Uri importTextMessage(String callingPkg, String address, int type, String text,
274                long timestampMillis, boolean seen, boolean read) throws RemoteException {
275            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import SMS message");
276            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
277                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
278                return null;
279            }
280            return getServiceGuarded().importTextMessage(
281                    callingPkg, address, type, text, timestampMillis, seen, read);
282        }
283
284        @Override
285        public Uri importMultimediaMessage(String callingPkg, byte[] pdu, String messageId,
286                long timestampSecs, boolean seen, boolean read) throws RemoteException {
287            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Import MMS message");
288            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
289                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
290                return null;
291            }
292            return getServiceGuarded().importMultimediaMessage(
293                    callingPkg, pdu, messageId, timestampSecs, seen, read);
294        }
295
296        @Override
297        public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
298                throws RemoteException {
299            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
300                    "Delete SMS/MMS message");
301            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
302                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
303                return false;
304            }
305            return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
306        }
307
308        @Override
309        public boolean deleteStoredConversation(String callingPkg, long conversationId)
310                throws RemoteException {
311            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
312            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
313                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
314                return false;
315            }
316            return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
317        }
318
319        @Override
320        public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
321                ContentValues statusValues) throws RemoteException {
322            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
323                    "Update SMS/MMS message");
324            return getServiceGuarded()
325                    .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
326        }
327
328        @Override
329        public boolean archiveStoredConversation(String callingPkg, long conversationId,
330                boolean archived) throws RemoteException {
331            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
332                    "Update SMS/MMS message");
333            return getServiceGuarded()
334                    .archiveStoredConversation(callingPkg, conversationId, archived);
335        }
336
337        @Override
338        public Uri addTextMessageDraft(String callingPkg, String address, String text)
339                throws RemoteException {
340            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add SMS draft");
341            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
342                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
343                return null;
344            }
345            return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
346        }
347
348        @Override
349        public Uri addMultimediaMessageDraft(String callingPkg, byte[] pdu) throws RemoteException {
350            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Add MMS draft");
351            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
352                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
353                return null;
354            }
355            return getServiceGuarded().addMultimediaMessageDraft(callingPkg, pdu);
356        }
357
358        @Override
359        public void sendStoredMessage(long subId, String callingPkg, Uri messageUri,
360                PendingIntent sentIntent) throws RemoteException {
361            mContext.enforceCallingPermission(Manifest.permission.SEND_SMS,
362                    "Send stored MMS message");
363            if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
364                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
365                return;
366            }
367            getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, sentIntent);
368        }
369
370        @Override
371        public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
372            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Set auto persist");
373            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
374                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
375                return;
376            }
377            getServiceGuarded().setAutoPersisting(callingPkg, enabled);
378        }
379
380        @Override
381        public boolean getAutoPersisting() throws RemoteException {
382            return getServiceGuarded().getAutoPersisting();
383        }
384    }
385}
386