MmsServiceBroker.java revision fa58ac09df51ddc7710a7b2837170dd3780aaf3a
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 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
57    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) {
283                // Silently fail AppOps failure due to not being the default SMS app
284                // while writing the TelephonyProvider
285                return FAKE_SMS_SENT_URI;
286            }
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) {
297                // Silently fail AppOps failure due to not being the default SMS app
298                // while writing the TelephonyProvider
299                return FAKE_MMS_SENT_URI;
300            }
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
338        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
347        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) {
352                // Silently fail AppOps failure due to not being the default SMS app
353                // while writing the TelephonyProvider
354                return FAKE_SMS_DRAFT_URI;
355            }
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) {
364                // Silently fail AppOps failure due to not being the default SMS app
365                // while writing the TelephonyProvider
366                return FAKE_MMS_DRAFT_URI;
367            }
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}
399