OSDN Git Service

DO NOT MERGE. Grant MMS Uri permissions as the calling UID.
[android-x86/frameworks-base.git] / services / core / java / com / android / server / MmsServiceBroker.java
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
17 package com.android.server;
18
19 import android.Manifest;
20 import android.app.ActivityManagerInternal;
21 import android.app.AppOpsManager;
22 import android.app.PendingIntent;
23 import android.content.ComponentName;
24 import android.content.ContentProvider;
25 import android.content.ContentValues;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.ServiceConnection;
29 import android.content.pm.PackageManager;
30 import android.net.Uri;
31 import android.os.Binder;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.IBinder;
35 import android.os.Message;
36 import android.os.RemoteException;
37 import android.os.SystemClock;
38 import android.os.UserHandle;
39 import android.service.carrier.CarrierMessagingService;
40 import android.telephony.SmsManager;
41 import android.telephony.TelephonyManager;
42 import android.util.Slog;
43
44 import com.android.internal.telephony.IMms;
45
46 import java.util.List;
47
48 /**
49  * This class is a proxy for MmsService APIs. We need this because MmsService runs
50  * in phone process and may crash anytime. This manages a connection to the actual
51  * MmsService and bridges the public SMS/MMS APIs with MmsService implementation.
52  */
53 public class MmsServiceBroker extends SystemService {
54     private static final String TAG = "MmsServiceBroker";
55
56     private static final ComponentName MMS_SERVICE_COMPONENT =
57             new ComponentName("com.android.mms.service", "com.android.mms.service.MmsService");
58
59     private static final int MSG_TRY_CONNECTING = 1;
60
61     private static final Uri FAKE_SMS_SENT_URI = Uri.parse("content://sms/sent/0");
62     private static final Uri FAKE_MMS_SENT_URI = Uri.parse("content://mms/sent/0");
63     private static final Uri FAKE_SMS_DRAFT_URI = Uri.parse("content://sms/draft/0");
64     private static final Uri FAKE_MMS_DRAFT_URI = Uri.parse("content://mms/draft/0");
65
66     private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
67     private static final long RETRY_DELAY_ON_DISCONNECTION_MS = 3 * 1000L; // 3 seconds
68
69     private Context mContext;
70     // The actual MMS service instance to invoke
71     private volatile IMms mService;
72
73     // Cached system service instances
74     private volatile AppOpsManager mAppOpsManager = null;
75     private volatile PackageManager mPackageManager = null;
76     private volatile TelephonyManager mTelephonyManager = null;
77
78     private final Handler mConnectionHandler = new Handler() {
79         @Override
80         public void handleMessage(Message msg) {
81             switch (msg.what) {
82                 case MSG_TRY_CONNECTING:
83                     tryConnecting();
84                     break;
85                 default:
86                     Slog.e(TAG, "Unknown message");
87             }
88         }
89     };
90
91     private ServiceConnection mConnection = new ServiceConnection() {
92         @Override
93         public void onServiceConnected(ComponentName name, IBinder service) {
94             Slog.i(TAG, "MmsService connected");
95             synchronized (MmsServiceBroker.this) {
96                 mService = IMms.Stub.asInterface(service);
97                 MmsServiceBroker.this.notifyAll();
98             }
99         }
100
101         @Override
102         public void onServiceDisconnected(ComponentName name) {
103             Slog.i(TAG, "MmsService unexpectedly disconnected");
104             synchronized (MmsServiceBroker.this) {
105                 mService = null;
106                 MmsServiceBroker.this.notifyAll();
107             }
108             // Retry connecting, but not too eager (with a delay)
109             // since it may come back by itself.
110             mConnectionHandler.sendMessageDelayed(
111                     mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING),
112                     RETRY_DELAY_ON_DISCONNECTION_MS);
113         }
114     };
115
116     // Instance of IMms for returning failure to service API caller,
117     // used when MmsService cannot be connected.
118     private final IMms mServiceStubForFailure = new IMms() {
119
120         @Override
121         public IBinder asBinder() {
122             return null;
123         }
124
125         @Override
126         public void sendMessage(int subId, String callingPkg, Uri contentUri, String locationUrl,
127                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
128             returnPendingIntentWithError(sentIntent);
129         }
130
131         @Override
132         public void downloadMessage(int subId, String callingPkg, String locationUrl,
133                 Uri contentUri, Bundle configOverrides, PendingIntent downloadedIntent)
134                 throws RemoteException {
135             returnPendingIntentWithError(downloadedIntent);
136         }
137
138         @Override
139         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
140             return null;
141         }
142
143         @Override
144         public Uri importTextMessage(String callingPkg, String address, int type, String text,
145                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
146             return null;
147         }
148
149         @Override
150         public Uri importMultimediaMessage(String callingPkg, Uri contentUri, String messageId,
151                 long timestampSecs, boolean seen, boolean read) throws RemoteException {
152             return null;
153         }
154
155         @Override
156         public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
157                 throws RemoteException {
158             return false;
159         }
160
161         @Override
162         public boolean deleteStoredConversation(String callingPkg, long conversationId)
163                 throws RemoteException {
164             return false;
165         }
166
167         @Override
168         public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
169                 ContentValues statusValues) throws RemoteException {
170             return false;
171         }
172
173         @Override
174         public boolean archiveStoredConversation(String callingPkg, long conversationId,
175                 boolean archived) throws RemoteException {
176             return false;
177         }
178
179         @Override
180         public Uri addTextMessageDraft(String callingPkg, String address, String text)
181                 throws RemoteException {
182             return null;
183         }
184
185         @Override
186         public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
187                 throws RemoteException {
188             return null;
189         }
190
191         @Override
192         public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
193                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
194             returnPendingIntentWithError(sentIntent);
195         }
196
197         @Override
198         public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
199             // Do nothing
200         }
201
202         @Override
203         public boolean getAutoPersisting() throws RemoteException {
204             return false;
205         }
206
207         private void returnPendingIntentWithError(PendingIntent pendingIntent) {
208             try {
209                 pendingIntent.send(mContext, SmsManager.MMS_ERROR_UNSPECIFIED, null);
210             } catch (PendingIntent.CanceledException e) {
211                 Slog.e(TAG, "Failed to return pending intent result", e);
212             }
213         }
214     };
215
216     public MmsServiceBroker(Context context) {
217         super(context);
218         mContext = context;
219         mService = null;
220     }
221
222     @Override
223     public void onStart() {
224         publishBinderService("imms", new BinderService());
225     }
226
227     public void systemRunning() {
228         Slog.i(TAG, "Delay connecting to MmsService until an API is called");
229     }
230
231     private void tryConnecting() {
232         Slog.i(TAG, "Connecting to MmsService");
233         synchronized (this) {
234             if (mService != null) {
235                 Slog.d(TAG, "Already connected");
236                 return;
237             }
238             final Intent intent = new Intent();
239             intent.setComponent(MMS_SERVICE_COMPONENT);
240             try {
241                 if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
242                     Slog.e(TAG, "Failed to bind to MmsService");
243                 }
244             } catch (SecurityException e) {
245                 Slog.e(TAG, "Forbidden to bind to MmsService", e);
246             }
247         }
248     }
249
250     private IMms getOrConnectService() {
251         synchronized (this) {
252             if (mService != null) {
253                 return mService;
254             }
255             // Service is not connected. Try blocking connecting.
256             Slog.w(TAG, "MmsService not connected. Try connecting...");
257             mConnectionHandler.sendMessage(
258                     mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
259             final long shouldEnd =
260                     SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
261             long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
262             while (waitTime > 0) {
263                 try {
264                     // TODO: consider using Java concurrent construct instead of raw object wait
265                     this.wait(waitTime);
266                 } catch (InterruptedException e) {
267                     Slog.w(TAG, "Connection wait interrupted", e);
268                 }
269                 if (mService != null) {
270                     // Success
271                     return mService;
272                 }
273                 // Calculate remaining waiting time to make sure we wait the full timeout period
274                 waitTime = shouldEnd - SystemClock.elapsedRealtime();
275             }
276             // Timed out. Something's really wrong.
277             Slog.e(TAG, "Can not connect to MmsService (timed out)");
278             return null;
279         }
280     }
281
282     /**
283      * Make sure to return a non-empty service instance. Return the connected MmsService
284      * instance, if not connected, try connecting. If fail to connect, return a fake service
285      * instance which returns failure to service caller.
286      *
287      * @return a non-empty service instance, real or fake
288      */
289     private IMms getServiceGuarded() {
290         final IMms service = getOrConnectService();
291         if (service != null) {
292             return service;
293         }
294         return mServiceStubForFailure;
295     }
296
297     private AppOpsManager getAppOpsManager() {
298         if (mAppOpsManager == null) {
299             mAppOpsManager = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
300         }
301         return mAppOpsManager;
302     }
303
304     private PackageManager getPackageManager() {
305         if (mPackageManager == null) {
306             mPackageManager = mContext.getPackageManager();
307         }
308         return mPackageManager;
309     }
310
311     private TelephonyManager getTelephonyManager() {
312         if (mTelephonyManager == null) {
313             mTelephonyManager = (TelephonyManager) mContext.getSystemService(
314                     Context.TELEPHONY_SERVICE);
315         }
316         return mTelephonyManager;
317     }
318
319     private String getCallingPackageName() {
320         final String[] packages = getPackageManager().getPackagesForUid(Binder.getCallingUid());
321         if (packages != null && packages.length > 0) {
322             return packages[0];
323         }
324         return "unknown";
325     }
326
327     // Service API calls implementation, proxied to the real MmsService in "com.android.mms.service"
328     private final class BinderService extends IMms.Stub {
329         private static final String PHONE_PACKAGE_NAME = "com.android.phone";
330
331         @Override
332         public void sendMessage(int subId, String callingPkg, Uri contentUri,
333                 String locationUrl, Bundle configOverrides, PendingIntent sentIntent)
334                         throws RemoteException {
335             Slog.d(TAG, "sendMessage() by " + callingPkg);
336             mContext.enforceCallingPermission(Manifest.permission.SEND_SMS, "Send MMS message");
337             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
338                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
339                 return;
340             }
341             contentUri = adjustUriForUserAndGrantPermission(contentUri,
342                     CarrierMessagingService.SERVICE_INTERFACE,
343                     Intent.FLAG_GRANT_READ_URI_PERMISSION);
344             getServiceGuarded().sendMessage(subId, callingPkg, contentUri, locationUrl,
345                     configOverrides, sentIntent);
346         }
347
348         @Override
349         public void downloadMessage(int subId, String callingPkg, String locationUrl,
350                 Uri contentUri, Bundle configOverrides,
351                 PendingIntent downloadedIntent) throws RemoteException {
352             Slog.d(TAG, "downloadMessage() by " + callingPkg);
353             mContext.enforceCallingPermission(Manifest.permission.RECEIVE_MMS,
354                     "Download MMS message");
355             if (getAppOpsManager().noteOp(AppOpsManager.OP_RECEIVE_MMS, Binder.getCallingUid(),
356                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
357                 return;
358             }
359             contentUri = adjustUriForUserAndGrantPermission(contentUri,
360                     CarrierMessagingService.SERVICE_INTERFACE,
361                     Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
362
363             getServiceGuarded().downloadMessage(subId, callingPkg, locationUrl, contentUri,
364                     configOverrides, downloadedIntent);
365         }
366
367         @Override
368         public Bundle getCarrierConfigValues(int subId) throws RemoteException {
369             Slog.d(TAG, "getCarrierConfigValues() by " + getCallingPackageName());
370             return getServiceGuarded().getCarrierConfigValues(subId);
371         }
372
373         @Override
374         public Uri importTextMessage(String callingPkg, String address, int type, String text,
375                 long timestampMillis, boolean seen, boolean read) throws RemoteException {
376             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
377                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
378                 // Silently fail AppOps failure due to not being the default SMS app
379                 // while writing the TelephonyProvider
380                 return FAKE_SMS_SENT_URI;
381             }
382             return getServiceGuarded().importTextMessage(
383                     callingPkg, address, type, text, timestampMillis, seen, read);
384         }
385
386         @Override
387         public Uri importMultimediaMessage(String callingPkg, Uri contentUri,
388                 String messageId, long timestampSecs, boolean seen, boolean read)
389                         throws RemoteException {
390             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
391                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
392                 // Silently fail AppOps failure due to not being the default SMS app
393                 // while writing the TelephonyProvider
394                 return FAKE_MMS_SENT_URI;
395             }
396             return getServiceGuarded().importMultimediaMessage(
397                     callingPkg, contentUri, messageId, timestampSecs, seen, read);
398         }
399
400         @Override
401         public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
402                 throws RemoteException {
403             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
404                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
405                 return false;
406             }
407             return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
408         }
409
410         @Override
411         public boolean deleteStoredConversation(String callingPkg, long conversationId)
412                 throws RemoteException {
413             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
414                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
415                 return false;
416             }
417             return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
418         }
419
420         @Override
421         public boolean updateStoredMessageStatus(String callingPkg, Uri messageUri,
422                 ContentValues statusValues) throws RemoteException {
423             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
424                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
425                 return false;
426             }
427             return getServiceGuarded()
428                     .updateStoredMessageStatus(callingPkg, messageUri, statusValues);
429         }
430
431         @Override
432         public boolean archiveStoredConversation(String callingPkg, long conversationId,
433                 boolean archived) throws RemoteException {
434             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
435                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
436                 return false;
437             }
438             return getServiceGuarded()
439                     .archiveStoredConversation(callingPkg, conversationId, archived);
440         }
441
442         @Override
443         public Uri addTextMessageDraft(String callingPkg, String address, String text)
444                 throws RemoteException {
445             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
446                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
447                 // Silently fail AppOps failure due to not being the default SMS app
448                 // while writing the TelephonyProvider
449                 return FAKE_SMS_DRAFT_URI;
450             }
451             return getServiceGuarded().addTextMessageDraft(callingPkg, address, text);
452         }
453
454         @Override
455         public Uri addMultimediaMessageDraft(String callingPkg, Uri contentUri)
456                 throws RemoteException {
457             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
458                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
459                 // Silently fail AppOps failure due to not being the default SMS app
460                 // while writing the TelephonyProvider
461                 return FAKE_MMS_DRAFT_URI;
462             }
463             return getServiceGuarded().addMultimediaMessageDraft(callingPkg, contentUri);
464         }
465
466         @Override
467         public void sendStoredMessage(int subId, String callingPkg, Uri messageUri,
468                 Bundle configOverrides, PendingIntent sentIntent) throws RemoteException {
469             if (getAppOpsManager().noteOp(AppOpsManager.OP_SEND_SMS, Binder.getCallingUid(),
470                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
471                 return;
472             }
473             getServiceGuarded().sendStoredMessage(subId, callingPkg, messageUri, configOverrides,
474                     sentIntent);
475         }
476
477         @Override
478         public void setAutoPersisting(String callingPkg, boolean enabled) throws RemoteException {
479             if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
480                     callingPkg) != AppOpsManager.MODE_ALLOWED) {
481                 return;
482             }
483             getServiceGuarded().setAutoPersisting(callingPkg, enabled);
484         }
485
486         @Override
487         public boolean getAutoPersisting() throws RemoteException {
488             return getServiceGuarded().getAutoPersisting();
489         }
490
491         /**
492          * Modifies the Uri to contain the caller's userId, if necessary.
493          * Grants the phone package on primary user permission to access the contentUri,
494          * even if the caller is not in the primary user.
495          *
496          * @param contentUri The Uri to adjust
497          * @param action The intent action used to find the associated carrier app
498          * @param permission The permission to add
499          * @return The adjusted Uri containing the calling userId.
500          */
501         private Uri adjustUriForUserAndGrantPermission(Uri contentUri, String action,
502                 int permission) {
503             final Intent grantIntent = new Intent();
504             grantIntent.setData(contentUri);
505             grantIntent.setFlags(permission);
506
507             final int callingUid = Binder.getCallingUid();
508             final int callingUserId = UserHandle.getCallingUserId();
509             if (callingUserId != UserHandle.USER_SYSTEM) {
510                 contentUri = ContentProvider.maybeAddUserId(contentUri, callingUserId);
511             }
512
513             long token = Binder.clearCallingIdentity();
514             try {
515                 LocalServices.getService(ActivityManagerInternal.class)
516                         .grantUriPermissionFromIntent(callingUid, PHONE_PACKAGE_NAME,
517                                 grantIntent, UserHandle.USER_SYSTEM);
518
519                 // Grant permission for the carrier app.
520                 Intent intent = new Intent(action);
521                 TelephonyManager telephonyManager =
522                     (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
523                 List<String> carrierPackages = telephonyManager.getCarrierPackageNamesForIntent(
524                         intent);
525                 if (carrierPackages != null && carrierPackages.size() == 1) {
526                     LocalServices.getService(ActivityManagerInternal.class)
527                             .grantUriPermissionFromIntent(callingUid, carrierPackages.get(0),
528                                     grantIntent, UserHandle.USER_SYSTEM);
529                 }
530             } finally {
531                 Binder.restoreCallingIdentity(token);
532             }
533             return contentUri;
534         }
535     }
536 }