1 /*
2  * Copyright (C) 2019 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.internal.telephony;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.content.pm.PackageManager;
26 import android.content.pm.ResolveInfo;
27 import android.os.AsyncResult;
28 import android.os.Handler;
29 import android.os.IBinder;
30 import android.os.Message;
31 import android.os.RemoteCallback;
32 import android.os.RemoteException;
33 import android.telephony.CellBroadcastService;
34 import android.telephony.ICellBroadcastService;
35 import android.text.TextUtils;
36 import android.util.LocalLog;
37 import android.util.Log;
38 import android.util.Pair;
39 
40 import com.android.cellbroadcastservice.CellBroadcastStatsLog;
41 import com.android.internal.telephony.cdma.SmsMessage;
42 
43 import java.io.FileDescriptor;
44 import java.io.PrintWriter;
45 import java.util.List;
46 
47 /**
48  * Manages a single binding to the CellBroadcastService from the platform. In mSIM cases callers
49  * should have one CellBroadcastServiceManager per phone, and the CellBroadcastServiceManager
50  * will handle the single binding.
51  */
52 public class CellBroadcastServiceManager {
53 
54     private static final String TAG = "CellBroadcastServiceManager";
55 
56     private String mCellBroadcastServicePackage;
57     private static CellBroadcastServiceConnection sServiceConnection;
58     private Handler mModuleCellBroadcastHandler = null;
59 
60     private Phone mPhone;
61     private Context mContext;
62 
63     private final LocalLog mLocalLog = new LocalLog(64);
64 
65     /** New SMS cell broadcast received as an AsyncResult. */
66     private static final int EVENT_NEW_GSM_SMS_CB = 0;
67     private static final int EVENT_NEW_CDMA_SMS_CB = 1;
68     private static final int EVENT_NEW_CDMA_SCP_MESSAGE = 2;
69     private boolean mEnabled = false;
70 
CellBroadcastServiceManager(Context context, Phone phone)71     public CellBroadcastServiceManager(Context context, Phone phone) {
72         Log.d(TAG, "CellBroadcastServiceManager created for phone " + phone.getPhoneId());
73         mContext = context;
74         mPhone = phone;
75     }
76 
cbMessagesDisabledByOem()77     private boolean cbMessagesDisabledByOem() {
78         if (mContext != null && mContext.getResources() != null) {
79             return mContext.getResources().getBoolean(
80                     com.android.internal.R.bool.config_disable_all_cb_messages);
81         } else {
82             return false;
83         }
84     }
85 
86     /**
87      * Send a GSM CB message to the CellBroadcastServiceManager's handler.
88      * @param m the message
89      */
sendGsmMessageToHandler(Message m)90     public void sendGsmMessageToHandler(Message m) {
91         if (cbMessagesDisabledByOem()) {
92             Log.d(TAG, "GSM CB message ignored - CB messages disabled by OEM.");
93             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
94                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__GSM,
95                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
96             return;
97         }
98         m.what = EVENT_NEW_GSM_SMS_CB;
99         mModuleCellBroadcastHandler.sendMessage(m);
100     }
101 
102     /**
103      * Send a CDMA CB message to the CellBroadcastServiceManager's handler.
104      * @param sms the SmsMessage to forward
105      */
sendCdmaMessageToHandler(SmsMessage sms)106     public void sendCdmaMessageToHandler(SmsMessage sms) {
107         if (cbMessagesDisabledByOem()) {
108             Log.d(TAG, "CDMA CB message ignored - CB messages disabled by OEM.");
109             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
110                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__CDMA,
111                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
112             return;
113         }
114         Message m = Message.obtain();
115         m.what = EVENT_NEW_CDMA_SMS_CB;
116         m.obj = sms;
117         mModuleCellBroadcastHandler.sendMessage(m);
118     }
119 
120     /**
121      * Send a CDMA Service Category Program message to the CellBroadcastServiceManager's handler.
122      * @param sms the SCP message
123      */
sendCdmaScpMessageToHandler(SmsMessage sms, RemoteCallback callback)124     public void sendCdmaScpMessageToHandler(SmsMessage sms, RemoteCallback callback) {
125         if (cbMessagesDisabledByOem()) {
126             Log.d(TAG, "CDMA SCP CB message ignored - CB messages disabled by OEM.");
127             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_FILTERED,
128                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__TYPE__CDMA_SPC,
129                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_FILTERED__FILTER__DISABLED_BY_OEM);
130             return;
131         }
132         Message m = Message.obtain();
133         m.what = EVENT_NEW_CDMA_SCP_MESSAGE;
134         m.obj = Pair.create(sms, callback);
135         mModuleCellBroadcastHandler.sendMessage(m);
136     }
137 
138     /**
139      * Enable the CB module. The CellBroadcastService will be bound to and CB messages from the
140      * RIL will be forwarded to the module.
141      */
enable()142     public void enable() {
143         initCellBroadcastServiceModule();
144     }
145 
146     /**
147      * Disable the CB module. The manager's handler will no longer receive CB messages from the RIL.
148      */
disable()149     public void disable() {
150         if (mEnabled == false) {
151             return;
152         }
153         mEnabled = false;
154         mPhone.mCi.unSetOnNewGsmBroadcastSms(mModuleCellBroadcastHandler);
155         if (sServiceConnection.mService != null) {
156             mContext.unbindService(sServiceConnection);
157         }
158     }
159 
160     /**
161      * The CellBroadcastServiceManager binds to an implementation of the CellBroadcastService
162      * specified in com.android.internal.R.string.cellbroadcast_default_package (typically the
163      * DefaultCellBroadcastService) and forwards cell broadcast messages to the service.
164      */
initCellBroadcastServiceModule()165     private void initCellBroadcastServiceModule() {
166         mEnabled = true;
167         if (sServiceConnection == null) {
168             sServiceConnection = new CellBroadcastServiceConnection();
169         }
170         mCellBroadcastServicePackage = getCellBroadcastServicePackage();
171         if (mCellBroadcastServicePackage != null) {
172             mModuleCellBroadcastHandler = new Handler() {
173                 @Override
174                 public void handleMessage(@NonNull Message msg) {
175                     if (!mEnabled) {
176                         Log.d(TAG, "CB module is disabled.");
177                         return;
178                     }
179                     if (sServiceConnection.mService == null) {
180                         final String errorMessage = "sServiceConnection.mService is null, ignoring message.";
181                         Log.d(TAG, errorMessage);
182                         CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
183                                 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
184                                 errorMessage);
185                         return;
186                     }
187                     try {
188                         ICellBroadcastService cellBroadcastService =
189                                 ICellBroadcastService.Stub.asInterface(
190                                         sServiceConnection.mService);
191                         if (msg.what == EVENT_NEW_GSM_SMS_CB) {
192                             mLocalLog.log("GSM SMS CB for phone " + mPhone.getPhoneId());
193                             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
194                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__GSM,
195                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
196                             cellBroadcastService.handleGsmCellBroadcastSms(mPhone.getPhoneId(),
197                                     (byte[]) ((AsyncResult) msg.obj).result);
198                         } else if (msg.what == EVENT_NEW_CDMA_SMS_CB) {
199                             mLocalLog.log("CDMA SMS CB for phone " + mPhone.getPhoneId());
200                             SmsMessage sms = (SmsMessage) msg.obj;
201                             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
202                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__CDMA,
203                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
204                             cellBroadcastService.handleCdmaCellBroadcastSms(mPhone.getPhoneId(),
205                                     sms.getEnvelopeBearerData(), sms.getEnvelopeServiceCategory());
206                         } else if (msg.what == EVENT_NEW_CDMA_SCP_MESSAGE) {
207                             mLocalLog.log("CDMA SCP message for phone " + mPhone.getPhoneId());
208                             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
209                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__CDMA_SPC,
210                                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
211                             Pair<SmsMessage, RemoteCallback> smsAndCallback =
212                                     (Pair<SmsMessage, RemoteCallback>) msg.obj;
213                             SmsMessage sms = smsAndCallback.first;
214                             RemoteCallback callback = smsAndCallback.second;
215                             cellBroadcastService.handleCdmaScpMessage(mPhone.getPhoneId(),
216                                     sms.getSmsCbProgramData(),
217                                     sms.getOriginatingAddress(),
218                                     callback);
219                         }
220                     } catch (RemoteException e) {
221                         final String errorMessage = "Failed to connect to default app: "
222                                 + mCellBroadcastServicePackage + " err: " + e.toString();
223                         Log.e(TAG, errorMessage);
224                         mLocalLog.log(errorMessage);
225                         CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
226                                 CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
227                                 errorMessage);
228                         mContext.unbindService(sServiceConnection);
229                         sServiceConnection = null;
230                     }
231                 }
232             };
233 
234             Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
235             intent.setPackage(mCellBroadcastServicePackage);
236             if (sServiceConnection.mService == null) {
237                 boolean serviceWasBound = mContext.bindService(intent, sServiceConnection,
238                         Context.BIND_AUTO_CREATE);
239                 Log.d(TAG, "serviceWasBound=" + serviceWasBound);
240                 if (!serviceWasBound) {
241                     final String errorMessage = "Unable to bind to service";
242                     Log.e(TAG, errorMessage);
243                     mLocalLog.log(errorMessage);
244                     CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
245                             CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
246                             errorMessage);
247                     return;
248                 }
249             } else {
250                 Log.d(TAG, "skipping bindService because connection already exists");
251             }
252             mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler, EVENT_NEW_GSM_SMS_CB,
253                     null);
254         } else {
255             final String errorMessage = "Unable to bind service; no cell broadcast service found";
256             Log.e(TAG, errorMessage);
257             mLocalLog.log(errorMessage);
258             CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
259                     CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
260                     errorMessage);
261         }
262     }
263 
264     /** Returns the package name of the cell broadcast service, or null if there is none. */
getCellBroadcastServicePackage()265     private String getCellBroadcastServicePackage() {
266         PackageManager packageManager = mContext.getPackageManager();
267         List<ResolveInfo> cbsPackages = packageManager.queryIntentServices(
268                 new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE),
269                 PackageManager.MATCH_SYSTEM_ONLY);
270         if (cbsPackages.size() != 1) {
271             Log.e(TAG, "getCellBroadcastServicePackageName: found " + cbsPackages.size()
272                     + " CBS packages");
273         }
274         for (ResolveInfo info : cbsPackages) {
275             if (info.serviceInfo == null) continue;
276             String packageName = info.serviceInfo.packageName;
277             if (!TextUtils.isEmpty(packageName)) {
278                 if (packageManager.checkPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
279                         packageName) == PackageManager.PERMISSION_GRANTED) {
280                     Log.d(TAG, "getCellBroadcastServicePackageName: " + packageName);
281                     return packageName;
282                 } else {
283                     Log.e(TAG, "getCellBroadcastServicePackageName: " + packageName
284                             + " does not have READ_PRIVILEGED_PHONE_STATE permission");
285                 }
286             } else {
287                 Log.e(TAG, "getCellBroadcastServicePackageName: found a CBS package but "
288                         + "packageName is null/empty");
289             }
290         }
291         Log.e(TAG, "getCellBroadcastServicePackageName: package name not found");
292         return null;
293     }
294 
295     private class CellBroadcastServiceConnection implements ServiceConnection {
296         IBinder mService;
297 
298         @Override
onServiceConnected(ComponentName className, IBinder service)299         public void onServiceConnected(ComponentName className, IBinder service) {
300             Log.d(TAG, "connected to CellBroadcastService");
301             this.mService = service;
302         }
303 
304         @Override
onServiceDisconnected(ComponentName arg0)305         public void onServiceDisconnected(ComponentName arg0) {
306             Log.d(TAG, "mICellBroadcastService has disconnected unexpectedly");
307             this.mService = null;
308         }
309 
310         @Override
onBindingDied(ComponentName name)311         public void onBindingDied(ComponentName name) {
312             Log.d(TAG, "Binding died");
313         }
314 
315         @Override
onNullBinding(ComponentName name)316         public void onNullBinding(ComponentName name) {
317             Log.d(TAG, "Null binding");
318         }
319     }
320 
321     /**
322      * Triggered with `adb shell dumpsys isms`
323      */
dump(FileDescriptor fd, PrintWriter pw, String[] args)324     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
325         pw.println("CellBroadcastServiceManager:");
326         pw.println(" mEnabled=" + mEnabled);
327         pw.println(" mCellBroadcastServicePackage=" + mCellBroadcastServicePackage);
328         if (mEnabled) {
329             try {
330                 if (sServiceConnection != null && sServiceConnection.mService != null) {
331                     sServiceConnection.mService.dump(fd, args);
332                 } else {
333                     pw.println(" sServiceConnection is null");
334                 }
335             } catch (RemoteException e) {
336                 pw.println(" mService.dump() threw RemoteException e: " + e.toString());
337             }
338         }
339         mLocalLog.dump(fd, pw, args);
340         pw.flush();
341     }
342 }
343