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

Commit af014b5d authored by Jordan Liu's avatar Jordan Liu Committed by android-build-merger
Browse files

Merge "Handle cell broadcasts with new module"

am: 66b9984d

Change-Id: Ie24e6eff936894dac54686bbad42d7bae5e8be30
parents a609060a 66b9984d
Loading
Loading
Loading
Loading
+171 −0
Original line number Original line 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 Original line Diff line number Diff line
@@ -36,7 +36,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.content.pm.IPackageManager;
import android.content.pm.IPackageManager;
import android.content.pm.UserInfo;
import android.database.Cursor;
import android.database.Cursor;
import android.database.SQLException;
import android.database.SQLException;
import android.net.Uri;
import android.net.Uri;
+3 −0
Original line number Original line Diff line number Diff line
@@ -551,6 +551,9 @@ public class SmsController extends ISmsImplBase {
        }
        }
    }
    }


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


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


import com.android.internal.telephony.CellBroadcastServiceManager;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.InboundSmsHandler;
import com.android.internal.telephony.Phone;
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.VisualVoicemailSmsFilter;
import com.android.internal.telephony.uicc.UsimServiceTable;
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
 * 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
 * the SmsProvider "raw" table and ACKing them to the SMSC. After each message has been
 */
 */
public class GsmInboundSmsHandler extends InboundSmsHandler {
public class GsmInboundSmsHandler extends InboundSmsHandler {


    private static BroadcastReceiver sTestBroadcastReceiver;
    /** Handler for SMS-PP data download messages to UICC. */
    /** Handler for SMS-PP data download messages to UICC. */
    private final UsimDataDownloadHandler mDataDownloadHandler;
    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.
     * Create a new GSM inbound SMS handler.
     */
     */
    private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
    private GsmInboundSmsHandler(Context context, SmsStorageMonitor storageMonitor,
            Phone phone) {
            Phone phone) {
        super("GsmInboundSmsHandler", context, storageMonitor, phone,
        super("GsmInboundSmsHandler", context, storageMonitor, phone, null);
                GsmCellBroadcastHandler.makeGsmCellBroadcastHandler(context, phone));
        phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
        phone.mCi.setOnNewGsmSms(getHandler(), EVENT_NEW_SMS, null);
        mDataDownloadHandler = new UsimDataDownloadHandler(phone.mCi, phone.getPhoneId());
        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
    @Override
    protected void onQuitting() {
    protected void onQuitting() {
        mPhone.mCi.unSetOnNewGsmSms(getHandler());
        mPhone.mCi.unSetOnNewGsmSms(getHandler());
        mCellBroadcastServiceManager.disable();
        mCellBroadcastHandler.dispose();
        mCellBroadcastHandler.dispose();


        if (DBG) log("unregistered for 3GPP SMS");
        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 true if this handler is for 3GPP2 messages; false for 3GPP format.
     *
     * @return false (3GPP)
     * @return false (3GPP)
     */
     */
    @Override
    @Override
@@ -158,6 +290,7 @@ public class GsmInboundSmsHandler extends InboundSmsHandler {


    /**
    /**
     * Send an acknowledge message.
     * Send an acknowledge message.
     *
     * @param success indicates that last message was successfully received.
     * @param success indicates that last message was successfully received.
     * @param result result code indicating any error
     * @param result result code indicating any error
     * @param response callback message sent when operation completes.
     * @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.
     * Convert Android result code to 3GPP SMS failure cause.
     *
     * @param rc the Android SMS intent result value
     * @param rc the Android SMS intent result value
     * @return 0 for success, or a 3GPP SMS failure cause 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(),
        mMetrics.writeIncomingVoiceMailSms(mPhone.getPhoneId(),
                android.telephony.SmsMessage.FORMAT_3GPP);
                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 Original line 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
        // verify that a broadcast receiver is registered for current user (user == null) based on
        // implementation in ContextFixture
        // implementation in ContextFixture
        verify(mContext).registerReceiverAsUser(any(BroadcastReceiver.class), eq((UserHandle)null),
        verify(mContext, times(2)).registerReceiverAsUser(any(BroadcastReceiver.class),
                any(IntentFilter.class), eq((String)null), eq((Handler)null));
                eq((UserHandle)null), any(IntentFilter.class), eq((String)null), eq((Handler)null));


        // wait for ScanRawTableThread
        // wait for ScanRawTableThread
        waitForMs(100);
        waitForMs(100);