Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 66b9984d authored by Jordan Liu's avatar Jordan Liu Committed by Gerrit Code Review
Browse files

Merge "Handle cell broadcasts with new module"

parents e50fa2be 746c1576
Loading
Loading
Loading
Loading
+171 −0
Original line number Diff line number Diff line
/*
 * 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.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CellBroadcastService;
import android.telephony.ICellBroadcastService;
import android.util.LocalLog;
import android.util.Log;

import java.io.FileDescriptor;
import java.io.PrintWriter;

/**
 * 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_SMS_CB = 0;
    private boolean mEnabled;

    public CellBroadcastServiceManager(Context context, Phone phone) {
        Log.d(TAG, "CellBroadcastServiceManager created for phone " + phone.getPhoneId());
        mContext = context;
        mPhone = phone;
    }

    /**
     * Send a CB message to the CellBroadcastServieManager's handler.
     * @param m the message
     */
    public void sendMessageToHandler(Message m) {
        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() {
        mEnabled = false;
        mPhone.mCi.unSetOnNewGsmBroadcastSms(mModuleCellBroadcastHandler);
        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;
                    }
                    try {
                        ICellBroadcastService cellBroadcastService =
                                ICellBroadcastService.Stub.asInterface(
                                        sServiceConnection.mService);
                        cellBroadcastService.handleGsmCellBroadcastSms(mPhone.getPhoneId(),
                                (byte[]) ((AsyncResult) msg.obj).result);
                    } catch (RemoteException e) {
                        Log.e(TAG, "Failed to connect to default app: "
                                + mCellBroadcastServicePackage + " err: " + e.toString());
                        mLocalLog.log("Failed to connect to default app: "
                                + mCellBroadcastServicePackage + " err: " + e.toString());
                        mContext.unbindService(sServiceConnection);
                        sServiceConnection = null;
                    }
                }
            };

            Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
            intent.setPackage(mCellBroadcastServicePackage);
            if (sServiceConnection.mService == null) {
                mContext.bindService(intent, sServiceConnection, Context.BIND_AUTO_CREATE);
            }
            mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler, EVENT_NEW_SMS_CB,
                    null);
        } else {
            Log.e(TAG, "Unable to bind service; no cell broadcast service found");
            mLocalLog.log("Unable to bind service; no cell broadcast service found");
        }
    }

    /** Returns the package name of the cell broadcast service, or null if there is none. */
    private String getCellBroadcastServicePackage() {
        return mContext.getResources().getString(
                com.android.internal.R.string.cellbroadcast_default_package);
    }

    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;
        }
    }

    /**
     * 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();
    }
}
+0 −1
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
+3 −0
Original line number Diff line number Diff line
@@ -551,6 +551,9 @@ public class SmsController extends ISmsImplBase {
        }
    }

    /**
     * Triggered by `adb shell dumpsys isms`
     */
    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        if (!checkDumpPermission(mContext, LOG_TAG, pw)) {
+145 −3
Original line number Diff line number Diff line
@@ -17,10 +17,15 @@
package com.android.internal.telephony.gsm;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncResult;
import android.os.Message;
import android.provider.Telephony.Sms.Intents;

import com.android.internal.telephony.CellBroadcastServiceManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
@@ -31,24 +36,149 @@ import com.android.internal.telephony.SmsStorageMonitor;
import com.android.internal.telephony.VisualVoicemailSmsFilter;
import com.android.internal.telephony.uicc.UsimServiceTable;

import java.io.FileDescriptor;
import java.io.PrintWriter;

/**
 * This class broadcasts incoming SMS messages to interested apps after storing them in
 * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
 */
public class GsmInboundSmsHandler extends InboundSmsHandler {

    private static BroadcastReceiver sTestBroadcastReceiver;
    /** Handler for SMS-PP data download messages to UICC. */
    private final UsimDataDownloadHandler mDataDownloadHandler;

    // When TEST_MODE is on we allow the test intent to trigger an SMS CB alert
    private static boolean sEnableCbModule = false;
    private static final boolean TEST_MODE = true; //STOPSHIP if true
    private static final String TEST_ACTION = "com.android.internal.telephony.gsm"
            + ".TEST_TRIGGER_CELL_BROADCAST";
    private static final String TOGGLE_CB_MODULE = "com.android.internal.telephony.gsm"
            + ".TOGGLE_CB_MODULE";
    private CellBroadcastServiceManager mCellBroadcastServiceManager;

    /**
     * Create a new GSM inbound SMS handler.
     */
    private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
            Phone phone) {
        super("GsmInboundSmsHandler", context, storageMonitor, phone,
                GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone));
        super("GsmInboundSmsHandler", context, storageMonitor, phone, null);
        phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
        mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
        mCellBroadcastServiceManager = new CellBroadcastServiceManager(context, phone);
        if (sEnableCbModule) {
            mCellBroadcastServiceManager.enable();
        } else {
            mCellBroadcastHandler = GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context,
                    phone);
        }

        if (TEST_MODE) {
            sTestBroadcastReceiver = new GsmCbTestBroadcastReceiver();
            IntentFilter filter = new IntentFilter();
            filter.addAction(TEST_ACTION);
            filter.addAction(TOGGLE_CB_MODULE);
            context.registerReceiver(sTestBroadcastReceiver, filter);
        }
    }


    /**
     * A broadcast receiver used for testing emergency cell broadcasts. To trigger test GSM cell
     * broadcasts with adb run e.g:
     *
     * adb shell am broadcast -a com.android.internal.telephony.gsm.TEST_TRIGGER_CELL_BROADCAST \
     * --es pdu_string  0000110011010D0A5BAE57CE770C531790E85C716CBF3044573065B9306757309707767 \
     * A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09000000000000000 \
     * 0000000000000
     *
     * adb shell am broadcast -a com.android.internal.telephony.gsm.TEST_TRIGGER_CELL_BROADCAST \
     * --es pdu_string  0000110011010D0A5BAE57CE770C531790E85C716CBF3044573065B9306757309707767 \
     * A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09000000000000000 \
     * 0000000000000 --ei phone_id 0
     *
     * adb shell am broadcast -a com.android.internal.telephony.gsm.TOGGLE_CB_MODULE
     *
     * adb shell am broadcast -a com.android.internal.telephony.gsm.TOGGLE_CB_MODULE -ez enable true
     */
    private class GsmCbTestBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            logd("Received test intent action=" + intent.getAction());
            if (intent.getAction() == TEST_ACTION) {
                byte[] smsPdu = intent.getByteArrayExtra("pdu");
                if (smsPdu == null) {
                    String pduString = intent.getStringExtra("pdu_string");
                    smsPdu = decodeHexString(pduString);
                }
                if (smsPdu == null) {
                    log("No pdu or pdu_string extra, ignoring CB test intent");
                    return;
                }

                // Return early if phone_id is explicilty included and does not match mPhone.
                // If phone_id extra is not included, continue.
                int phoneId = mPhone.getPhoneId();
                if (intent.getIntExtra("phone_id", phoneId) != phoneId) {
                    return;
                }
                Message m = Message.obtain();
                AsyncResult.forMessage(m, smsPdu, null);
                if (sEnableCbModule) {
                    mCellBroadcastServiceManager.sendMessageToHandler(m);
                } else {
                    m.setWhat(GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE);
                    mCellBroadcastHandler.sendMessage(m);
                }
            } else if (intent.getAction() == TOGGLE_CB_MODULE) {
                if (intent.hasExtra("enable")) {
                    sEnableCbModule = intent.getBooleanExtra("enable", false);
                } else {
                    sEnableCbModule = !sEnableCbModule;
                }
                if (sEnableCbModule) {
                    log("enabling CB module");
                    mPhone.mCi.unSetOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler());
                    mCellBroadcastServiceManager.enable();
                } else {
                    log("enabling legacy platform CB handling");
                    mCellBroadcastServiceManager.disable();
                    if (mCellBroadcastHandler == null) {
                        mCellBroadcastHandler =
                                GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context,
                                        mPhone);
                    }
                    mPhone.mCi.setOnNewGsmBroadcastSms(mCellBroadcastHandler.getHandler(),
                            GsmCellBroadcastHandler.EVENT_NEW_SMS_MESSAGE, null);
                }
            }
        }
    }

    private byte[] decodeHexString(String hexString) {
        if (hexString == null || hexString.length() % 2 == 1) {
            return null;
        }
        byte[] bytes = new byte[hexString.length() / 2];
        for (int i = 0; i < hexString.length(); i += 2) {
            bytes[i / 2] = hexToByte(hexString.substring(i, i + 2));
        }
        return bytes;
    }

    private byte hexToByte(String hexString) {
        int firstDigit = toDigit(hexString.charAt(0));
        int secondDigit = toDigit(hexString.charAt(1));
        return (byte) ((firstDigit << 4) + secondDigit);
    }

    private int toDigit(char hexChar) {
        int digit = Character.digit(hexChar, 16);
        if (digit == -1) {
            return 0;
        }
        return digit;
    }

    /**
@@ -57,6 +187,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
    @Override
    protected void onQuitting() {
        mPhone.mCi.unSetOnNewGsmSms(getHandler());
        mCellBroadcastServiceManager.disable();
        mCellBroadcastHandler.dispose();

        if (DBG) log("unregistered for 3GPP SMS");
@@ -75,6 +206,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {

    /**
     * Return true if this handler is for 3GPP2 messages; false for 3GPP format.
     *
     * @return false (3GPP)
     */
    @Override
@@ -158,6 +290,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {

    /**
     * Send an acknowledge message.
     *
     * @param success indicates that last message was successfully received.
     * @param result result code indicating any error
     * @param response callback message sent when operation completes.
@@ -169,6 +302,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {

    /**
     * Convert Android result code to 3GPP SMS failure cause.
     *
     * @param rc the Android SMS intent result value
     * @return 0 for success, or a 3GPP SMS failure cause value
     */
@@ -201,4 +335,12 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {
        mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
                android.telephony.SmsMessage.FORMAT_3GPP);
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        super.dump(fd, pw, args);
        if (mCellBroadcastServiceManager != null) {
            mCellBroadcastServiceManager.dump(fd, pw, args);
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -770,8 +770,8 @@ public class GsmInboundSmsHandlerTest extends TelephonyTest {

        // verify that a broadcast receiver is registered for current user (user == null) based on
        // implementation in ContextFixture
        verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq((UserHandle)null),
                any(IntentFilter.class), eq((String)null), eq((Handler)null));
        verify(mContext, times(2)).registerReceiverAsUser(any(BroadcastReceiver.class),
                eq((UserHandle)null), any(IntentFilter.class), eq((String)null), eq((Handler)null));

        // wait for ScanRawTableThread
        waitForMs(100);