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