blob: a991153e27350d8bcd6a1d2843c813a1b4b3d152 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.telephony;
import android.Manifest;
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.telephony.CellBroadcastService;
import android.telephony.ICellBroadcastService;
import android.text.TextUtils;
import android.util.LocalLog;
import android.util.Log;
import android.util.Pair;
import com.android.cellbroadcastservice.CellBroadcastStatsLog;
import com.android.internal.telephony.cdma.SmsMessage;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.List;
/**
* Manages a single binding to the CellBroadcastService from the platform. In mSIM cases callers
* should have one CellBroadcastServiceManager per phone, and the CellBroadcastServiceManager
* will handle the single binding.
*/
public class CellBroadcastServiceManager {
private static final String TAG = "CellBroadcastServiceManager";
private String mCellBroadcastServicePackage;
private static CellBroadcastServiceConnection sServiceConnection;
private Handler mModuleCellBroadcastHandler = null;
private Phone mPhone;
private Context mContext;
private final LocalLog mLocalLog = new LocalLog(100);
/** New SMS cell broadcast received as an AsyncResult. */
private static final int EVENT_NEW_GSM_SMS_CB = 0;
private static final int EVENT_NEW_CDMA_SMS_CB = 1;
private static final int EVENT_NEW_CDMA_SCP_MESSAGE = 2;
private boolean mEnabled = false;
public CellBroadcastServiceManager(Context context, Phone phone) {
Log.d(TAG, "CellBroadcastServiceManager created for phone " + phone.getPhoneId());
mContext = context;
mPhone = phone;
}
/**
* Send a GSM CB message to the CellBroadcastServiceManager's handler.
* @param m the message
*/
public void sendGsmMessageToHandler(Message m) {
m.what = EVENT_NEW_GSM_SMS_CB;
mModuleCellBroadcastHandler.sendMessage(m);
}
/**
* Send a CDMA CB message to the CellBroadcastServiceManager's handler.
* @param sms the SmsMessage to forward
*/
public void sendCdmaMessageToHandler(SmsMessage sms) {
Message m = Message.obtain();
m.what = EVENT_NEW_CDMA_SMS_CB;
m.obj = sms;
mModuleCellBroadcastHandler.sendMessage(m);
}
/**
* Send a CDMA Service Category Program message to the CellBroadcastServiceManager's handler.
* @param sms the SCP message
*/
public void sendCdmaScpMessageToHandler(SmsMessage sms, RemoteCallback callback) {
Message m = Message.obtain();
m.what = EVENT_NEW_CDMA_SCP_MESSAGE;
m.obj = Pair.create(sms, callback);
mModuleCellBroadcastHandler.sendMessage(m);
}
/**
* Enable the CB module. The CellBroadcastService will be bound to and CB messages from the
* RIL will be forwarded to the module.
*/
public void enable() {
initCellBroadcastServiceModule();
}
/**
* Disable the CB module. The manager's handler will no longer receive CB messages from the RIL.
*/
public void disable() {
if (mEnabled == false) {
return;
}
mEnabled = false;
mPhone.mCi.unSetOnNewGsmBroadcastSms(mModuleCellBroadcastHandler);
if (sServiceConnection.mService != null) {
mContext.unbindService(sServiceConnection);
}
}
/**
* The CellBroadcastServiceManager binds to an implementation of the CellBroadcastService
* specified in com.android.internal.R.string.cellbroadcast_default_package (typically the
* DefaultCellBroadcastService) and forwards cell broadcast messages to the service.
*/
private void initCellBroadcastServiceModule() {
mEnabled = true;
if (sServiceConnection == null) {
sServiceConnection = new CellBroadcastServiceConnection();
}
mCellBroadcastServicePackage = getCellBroadcastServicePackage();
if (mCellBroadcastServicePackage != null) {
mModuleCellBroadcastHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (!mEnabled) {
Log.d(TAG, "CB module is disabled.");
return;
}
if (sServiceConnection.mService == null) {
final String errorMessage = "sServiceConnection.mService is null, ignoring message.";
Log.d(TAG, errorMessage);
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
errorMessage);
return;
}
try {
ICellBroadcastService cellBroadcastService =
ICellBroadcastService.Stub.asInterface(
sServiceConnection.mService);
if (msg.what == EVENT_NEW_GSM_SMS_CB) {
mLocalLog.log("GSM SMS CB for phone " + mPhone.getPhoneId());
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__GSM,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
cellBroadcastService.handleGsmCellBroadcastSms(mPhone.getPhoneId(),
(byte[]) ((AsyncResult) msg.obj).result);
} else if (msg.what == EVENT_NEW_CDMA_SMS_CB) {
mLocalLog.log("CDMA SMS CB for phone " + mPhone.getPhoneId());
SmsMessage sms = (SmsMessage) msg.obj;
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__CDMA,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
cellBroadcastService.handleCdmaCellBroadcastSms(mPhone.getPhoneId(),
sms.getEnvelopeBearerData(), sms.getEnvelopeServiceCategory());
} else if (msg.what == EVENT_NEW_CDMA_SCP_MESSAGE) {
mLocalLog.log("CDMA SCP message for phone " + mPhone.getPhoneId());
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_REPORTED,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__TYPE__CDMA_SPC,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_REPORTED__SOURCE__FRAMEWORK);
Pair<SmsMessage, RemoteCallback> smsAndCallback =
(Pair<SmsMessage, RemoteCallback>) msg.obj;
SmsMessage sms = smsAndCallback.first;
RemoteCallback callback = smsAndCallback.second;
cellBroadcastService.handleCdmaScpMessage(mPhone.getPhoneId(),
sms.getSmsCbProgramData(),
sms.getOriginatingAddress(),
callback);
}
} catch (RemoteException e) {
final String errorMessage = "Failed to connect to default app: "
+ mCellBroadcastServicePackage + " err: " + e.toString();
Log.e(TAG, errorMessage);
mLocalLog.log(errorMessage);
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
errorMessage);
mContext.unbindService(sServiceConnection);
sServiceConnection = null;
}
}
};
Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
intent.setPackage(mCellBroadcastServicePackage);
if (sServiceConnection.mService == null) {
boolean serviceWasBound = mContext.bindService(intent, sServiceConnection,
Context.BIND_AUTO_CREATE);
Log.d(TAG, "serviceWasBound=" + serviceWasBound);
if (!serviceWasBound) {
final String errorMessage = "Unable to bind to service";
Log.e(TAG, errorMessage);
mLocalLog.log(errorMessage);
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
errorMessage);
return;
}
} else {
Log.d(TAG, "skipping bindService because connection already exists");
}
mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler, EVENT_NEW_GSM_SMS_CB,
null);
} else {
final String errorMessage = "Unable to bind service; no cell broadcast service found";
Log.e(TAG, errorMessage);
mLocalLog.log(errorMessage);
CellBroadcastStatsLog.write(CellBroadcastStatsLog.CB_MESSAGE_ERROR,
CellBroadcastStatsLog.CELL_BROADCAST_MESSAGE_ERROR__TYPE__NO_CONNECTION_TO_CB_SERVICE,
errorMessage);
}
}
/** Returns the package name of the cell broadcast service, or null if there is none. */
private String getCellBroadcastServicePackage() {
PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> cbsPackages = packageManager.queryIntentServices(
new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE),
PackageManager.MATCH_SYSTEM_ONLY);
if (cbsPackages.size() != 1) {
Log.e(TAG, "getCellBroadcastServicePackageName: found " + cbsPackages.size()
+ " CBS packages");
}
for (ResolveInfo info : cbsPackages) {
if (info.serviceInfo == null) continue;
String packageName = info.serviceInfo.packageName;
if (!TextUtils.isEmpty(packageName)) {
if (packageManager.checkPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
packageName) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "getCellBroadcastServicePackageName: " + packageName);
return packageName;
} else {
Log.e(TAG, "getCellBroadcastServicePackageName: " + packageName
+ " does not have READ_PRIVILEGED_PHONE_STATE permission");
}
} else {
Log.e(TAG, "getCellBroadcastServicePackageName: found a CBS package but "
+ "packageName is null/empty");
}
}
Log.e(TAG, "getCellBroadcastServicePackageName: package name not found");
return null;
}
private class CellBroadcastServiceConnection implements ServiceConnection {
IBinder mService;
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
Log.d(TAG, "connected to CellBroadcastService");
this.mService = service;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
Log.d(TAG, "mICellBroadcastService has disconnected unexpectedly");
this.mService = null;
}
@Override
public void onBindingDied(ComponentName name) {
Log.d(TAG, "Binding died");
}
@Override
public void onNullBinding(ComponentName name) {
Log.d(TAG, "Null binding");
}
}
/**
* Triggered with `adb shell dumpsys isms`
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
pw.println("CellBroadcastServiceManager:");
pw.println(" mEnabled=" + mEnabled);
pw.println(" mCellBroadcastServicePackage=" + mCellBroadcastServicePackage);
mLocalLog.dump(fd, pw, args);
pw.flush();
}
}