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

Commit 4f047c91 authored by Jordan Liu's avatar Jordan Liu Committed by Jack Yu
Browse files

Remove unused cell broadcast code

Framework handling of cell broadcasts is removed in favor of the
CellBroadcastService module. This CL removes old code.

Bug: 135956699
Test: manual
Merged-In: I0151ca0d93a61f69dd8b3066a8c60089876b6da5
Change-Id: I0151ca0d93a61f69dd8b3066a8c60089876b6da5
(cherry picked from commit 158fea7f)
parent 18841a20
Loading
Loading
Loading
Loading
+0 −459
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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 static android.provider.Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Activity;
import android.app.AppOpsManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.UserHandle;
import android.provider.Settings;
import android.provider.Telephony;
import android.provider.Telephony.CellBroadcasts;
import android.telephony.CbGeoUtils;
import android.telephony.CbGeoUtils.Geometry;
import android.telephony.CbGeoUtils.LatLng;
import android.telephony.SmsCbMessage;
import android.telephony.SubscriptionManager;
import android.text.format.DateUtils;
import android.util.LocalLog;
import android.util.Log;

import com.android.internal.telephony.metrics.TelephonyMetrics;
import com.android.internal.telephony.util.TelephonyUtils;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
 * completes and our result receiver is called.
 */
public class CellBroadcastHandler extends WakeLockStateMachine {
    private static final String EXTRA_MESSAGE = "message";

    private final LocalLog mLocalLog = new LocalLog(100);

    protected static final Uri CELL_BROADCAST_URI = Uri.parse("content://cellbroadcasts");

    /** Uses to request the location update. */
    public final LocationRequester mLocationRequester;

    private CellBroadcastHandler(Context context, Phone phone) {
        this("CellBroadcastHandler", context, phone);
    }

    protected CellBroadcastHandler(String debugTag, Context context, Phone phone) {
        super(debugTag, context, phone);
        mLocationRequester = new LocationRequester(
                context,
                (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE),
                getHandler().getLooper());
    }

    /**
     * Create a new CellBroadcastHandler.
     * @param context the context to use for dispatching Intents
     * @return the new handler
     */
    public static CellBroadcastHandler makeCellBroadcastHandler(Context context, Phone phone) {
        CellBroadcastHandler handler = new CellBroadcastHandler(context, phone);
        handler.start();
        return handler;
    }

    /**
     * Handle Cell Broadcast messages from {@code CdmaInboundSmsHandler}.
     * 3GPP-format Cell Broadcast messages sent from radio are handled in the subclass.
     *
     * @param message the message to process
     * @return true if need to wait for geo-fencing or an ordered broadcast was sent.
     */
    @Override
    protected boolean handleSmsMessage(Message message) {
        if (message.obj instanceof SmsCbMessage) {
            handleBroadcastSms((SmsCbMessage) message.obj);
            return true;
        } else {
            loge("handleMessage got object of type: " + message.obj.getClass().getName());
            return false;
        }
    }

    /**
     * Dispatch a Cell Broadcast message to listeners.
     * @param message the Cell Broadcast to broadcast
     */
    protected void handleBroadcastSms(SmsCbMessage message) {
        // Log Cellbroadcast msg received event
        TelephonyMetrics metrics = TelephonyMetrics.getInstance();
        metrics.writeNewCBSms(mPhone.getPhoneId(), message.getMessageFormat(),
                message.getMessagePriority(), message.isCmasMessage(), message.isEtwsMessage(),
                message.getServiceCategory(), message.getSerialNumber(),
                System.currentTimeMillis());

        // TODO: Database inserting can be time consuming, therefore this should be changed to
        // asynchronous.
        ContentValues cv = message.getContentValues();
        Uri uri = mContext.getContentResolver().insert(CELL_BROADCAST_URI, cv);

        if (message.needGeoFencingCheck()) {
            if (DBG) {
                log("Request location update for geo-fencing. serialNumber = "
                        + message.getSerialNumber());
            }

            requestLocationUpdate(location -> {
                if (location == null) {
                    // Broadcast the message directly if the location is not available.
                    broadcastMessage(message, uri);
                } else {
                    performGeoFencing(message, uri, message.getGeometries(), location);
                }
            }, message.getMaximumWaitingDuration());
        } else {
            if (DBG) {
                log("Broadcast the message directly because no geo-fencing required, "
                        + "serialNumber = " + message.getSerialNumber()
                        + " needGeoFencing = " + message.needGeoFencingCheck());
            }
            broadcastMessage(message, uri);
        }
    }

    /**
     * Perform a geo-fencing check for {@code message}. Broadcast the {@code message} if the
     * {@code location} is inside the {@code broadcastArea}.
     * @param message the message need to geo-fencing check
     * @param uri the message's uri
     * @param broadcastArea the broadcast area of the message
     * @param location current location
     */
    protected void performGeoFencing(SmsCbMessage message, Uri uri, List<Geometry> broadcastArea,
            LatLng location) {

        if (DBG) {
            logd("Perform geo-fencing check for message identifier = "
                    + message.getServiceCategory()
                    + " serialNumber = " + message.getSerialNumber());
        }

        for (Geometry geo : broadcastArea) {
            if (geo.contains(location)) {
                broadcastMessage(message, uri);
                return;
            }
        }

        if (DBG) {
            logd("Device location is outside the broadcast area "
                    + CbGeoUtils.encodeGeometriesToString(broadcastArea));
        }

        sendMessage(EVENT_BROADCAST_NOT_REQUIRED);
    }

    /**
     * Request a single location update.
     * @param callback a callback will be called when the location is available.
     * @param maximumWaitTimeSec the maximum wait time of this request. If location is not updated
     * within the maximum wait time, {@code callback#onLocationUpadte(null)} will be called.
     */
    protected void requestLocationUpdate(LocationUpdateCallback callback, int maximumWaitTimeSec) {
        mLocationRequester.requestLocationUpdate(callback, maximumWaitTimeSec);
    }

    /**
     * Broadcast a list of cell broadcast messages.
     * @param cbMessages a list of cell broadcast message.
     * @param cbMessageUris the corresponding {@link Uri} of the cell broadcast messages.
     */
    protected void broadcastMessage(List<SmsCbMessage> cbMessages, List<Uri> cbMessageUris) {
        for (int i = 0; i < cbMessages.size(); i++) {
            broadcastMessage(cbMessages.get(i), cbMessageUris.get(i));
        }
    }

    /**
     * Broadcast the {@code message} to the applications.
     * @param message a message need to broadcast
     * @param messageUri message's uri
     */
    protected void broadcastMessage(@NonNull SmsCbMessage message, @Nullable Uri messageUri) {
        String receiverPermission;
        String appOp;
        String msg;
        Intent intent;
        if (message.isEmergencyMessage()) {
            msg = "Dispatching emergency SMS CB, SmsCbMessage is: " + message;
            log(msg);
            mLocalLog.log(msg);
            intent = new Intent(Telephony.Sms.Intents.ACTION_SMS_EMERGENCY_CB_RECEIVED);
            //Emergency alerts need to be delivered with high priority
            intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
            appOp = AppOpsManager.OPSTR_RECEIVE_EMERGENCY_BROADCAST;

            intent.putExtra(EXTRA_MESSAGE, message);
            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());

            if (TelephonyUtils.IS_DEBUGGABLE) {
                // Send additional broadcast intent to the specified package. This is only for sl4a
                // automation tests.
                final String additionalPackage = Settings.Secure.getString(
                        mContext.getContentResolver(), CMAS_ADDITIONAL_BROADCAST_PKG);
                if (additionalPackage != null) {
                    Intent additionalIntent = new Intent(intent);
                    additionalIntent.setPackage(additionalPackage);
                    try {
                        mContext.createPackageContextAsUser(
                                mContext.getPackageName(), 0, UserHandle.ALL)
                                .sendOrderedBroadcast(additionalIntent, receiverPermission, appOp,
                                        null /* resultReceiver */, getHandler(), Activity.RESULT_OK,
                                        null /* initialData */, null /* initialExtras */);
                    } catch (PackageManager.NameNotFoundException ignored) {
                    }
                }
            }

            String[] pkgs = mContext.getResources().getStringArray(
                    com.android.internal.R.array.config_defaultCellBroadcastReceiverPkgs);
            mReceiverCount.addAndGet(pkgs.length);
            for (String pkg : pkgs) {
                // Explicitly send the intent to all the configured cell broadcast receivers.
                intent.setPackage(pkg);
                try {
                    mContext.createPackageContextAsUser(
                            mContext.getPackageName(), 0, UserHandle.ALL)
                            .sendOrderedBroadcast(intent, receiverPermission, appOp, mReceiver,
                                    getHandler(), Activity.RESULT_OK, null /* initialData */,
                                    null /* initialExtras */);
                } catch (PackageManager.NameNotFoundException ignored) {
                }
            }
        } else {
            msg = "Dispatching SMS CB, SmsCbMessage is: " + message;
            log(msg);
            mLocalLog.log(msg);
            intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
            // Send implicit intent since there are various 3rd party carrier apps listen to
            // this intent.
            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
            receiverPermission = Manifest.permission.RECEIVE_SMS;
            appOp = AppOpsManager.OPSTR_RECEIVE_SMS;

            intent.putExtra(EXTRA_MESSAGE, message);
            SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());

            mReceiverCount.incrementAndGet();
            try {
                mContext.createPackageContextAsUser(mContext.getPackageName(), 0, UserHandle.ALL)
                        .sendOrderedBroadcast(intent, receiverPermission, appOp, mReceiver,
                                getHandler(), Activity.RESULT_OK, null /* initialData */,
                                null /* initialExtras */);
            } catch (PackageManager.NameNotFoundException ignored) {
            }
        }

        if (messageUri != null) {
            ContentValues cv = new ContentValues();
            cv.put(CellBroadcasts.MESSAGE_BROADCASTED, 1);
            mContext.getContentResolver().update(CELL_BROADCAST_URI, cv,
                    CellBroadcasts._ID + "=?", new String[] {messageUri.getLastPathSegment()});
        }
    }

    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("CellBroadcastHandler:");
        mLocalLog.dump(fd, pw, args);
        pw.flush();
    }

    /** The callback interface of a location request. */
    public interface LocationUpdateCallback {
        /**
         * Call when the location update is available.
         * @param location a location in (latitude, longitude) format, or {@code null} if the
         * location service is not available.
         */
        void onLocationUpdate(@Nullable LatLng location);
    }

    private static final class LocationRequester {
        private static final String TAG = LocationRequester.class.getSimpleName();

        /**
         * Use as the default maximum wait time if the cell broadcast doesn't specify the value.
         * Most of the location request should be responded within 20 seconds.
         */
        private static final int DEFAULT_MAXIMUM_WAIT_TIME_SEC = 20;

        /**
         * Trigger this event when the {@link LocationManager} is not responded within the given
         * time.
         */
        private static final int EVENT_LOCATION_REQUEST_TIMEOUT = 1;

        /** Request a single location update. */
        private static final int EVENT_REQUEST_LOCATION_UPDATE = 2;

        /**
         * Request location update from network or gps location provider. Network provider will be
         * used if available, otherwise use the gps provider.
         */
        private static final List<String> LOCATION_PROVIDERS = Arrays.asList(
                LocationManager.NETWORK_PROVIDER, LocationManager.GPS_PROVIDER);

        private final LocationManager mLocationManager;
        private final Looper mLooper;
        private final List<LocationUpdateCallback> mCallbacks;
        private final Context mContext;
        private Handler mLocationHandler;

        LocationRequester(Context context, LocationManager locationManager, Looper looper) {
            mLocationManager = locationManager;
            mLooper = looper;
            mCallbacks = new ArrayList<>();
            mContext = context;
            mLocationHandler = new LocationHandler(looper);
        }

        /**
         * Request a single location update. If the location is not available, a callback with
         * {@code null} location will be called immediately.
         *
         * @param callback a callback to the the response when the location is available
         * @param maximumWaitTimeSec the maximum wait time of this request. If location is not
         * updated within the maximum wait time, {@code callback#onLocationUpadte(null)} will be
         * called.
         */
        void requestLocationUpdate(@NonNull LocationUpdateCallback callback,
                int maximumWaitTimeSec) {
            mLocationHandler.obtainMessage(EVENT_REQUEST_LOCATION_UPDATE, maximumWaitTimeSec,
                    0 /* arg2 */, callback).sendToTarget();
        }

        private void onLocationUpdate(@Nullable LatLng location) {
            for (LocationUpdateCallback callback : mCallbacks) {
                callback.onLocationUpdate(location);
            }
            mCallbacks.clear();
        }

        private void requestLocationUpdateInternal(@NonNull LocationUpdateCallback callback,
                int maximumWaitTimeSec) {
            if (DBG) Log.d(TAG, "requestLocationUpdate");
            if (!isLocationServiceAvailable()) {
                if (DBG) {
                    Log.d(TAG, "Can't request location update because of no location permission");
                }
                callback.onLocationUpdate(null);
                return;
            }

            if (!mLocationHandler.hasMessages(EVENT_LOCATION_REQUEST_TIMEOUT)) {
                if (maximumWaitTimeSec == SmsCbMessage.MAXIMUM_WAIT_TIME_NOT_SET) {
                    maximumWaitTimeSec = DEFAULT_MAXIMUM_WAIT_TIME_SEC;
                }
                mLocationHandler.sendMessageDelayed(
                        mLocationHandler.obtainMessage(EVENT_LOCATION_REQUEST_TIMEOUT),
                        maximumWaitTimeSec * DateUtils.SECOND_IN_MILLIS);
            }

            mCallbacks.add(callback);

            for (String provider : LOCATION_PROVIDERS) {
                if (mLocationManager.isProviderEnabled(provider)) {
                    mLocationManager.requestSingleUpdate(provider, mLocationListener, mLooper);
                    break;
                }
            }
        }

        private boolean isLocationServiceAvailable() {
            if (!hasPermission(Manifest.permission.ACCESS_FINE_LOCATION)
                    && !hasPermission(Manifest.permission.ACCESS_COARSE_LOCATION)) return false;
            for (String provider : LOCATION_PROVIDERS) {
                if (mLocationManager.isProviderEnabled(provider)) return true;
            }
            return false;
        }

        private boolean hasPermission(String permission) {
        return mContext.checkPermission(permission, Process.myPid(), Process.myUid())
                    == PackageManager.PERMISSION_GRANTED;
        }

        private final LocationListener mLocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                mLocationHandler.removeMessages(EVENT_LOCATION_REQUEST_TIMEOUT);
                onLocationUpdate(new LatLng(location.getLatitude(), location.getLongitude()));
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {}

            @Override
            public void onProviderEnabled(String provider) {}

            @Override
            public void onProviderDisabled(String provider) {}
        };

        private final class LocationHandler extends Handler {
            LocationHandler(Looper looper) {
                super(looper);
            }

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case EVENT_LOCATION_REQUEST_TIMEOUT:
                        if (DBG) Log.d(TAG, "location request timeout");
                        onLocationUpdate(null);
                        break;
                    case EVENT_REQUEST_LOCATION_UPDATE:
                        requestLocationUpdateInternal((LocationUpdateCallback) msg.obj, msg.arg1);
                        break;
                    default:
                        Log.e(TAG, "Unsupported message type " + msg.what);
                }
            }
        }
    }
}
+2 −28
Original line number Diff line number Diff line
@@ -224,9 +224,6 @@ public abstract class InboundSmsHandler extends StateMachine {
    @UnsupportedAppUsage
    protected Phone mPhone;

    @UnsupportedAppUsage
    protected CellBroadcastHandler mCellBroadcastHandler;

    @UnsupportedAppUsage
    private UserManager mUserManager;

@@ -237,8 +234,6 @@ public abstract class InboundSmsHandler extends StateMachine {
    @UnsupportedAppUsage
    IDeviceIdleController mDeviceIdleController;

    protected static boolean sEnableCbModule = true;

    protected CellBroadcastServiceManager mCellBroadcastServiceManager;

    // Delete permanently from raw table
@@ -263,13 +258,12 @@ public abstract class InboundSmsHandler extends StateMachine {
     * @param storageMonitor the SmsStorageMonitor to check for storage availability
     */
    protected InboundSmsHandler(String name, Context context, SmsStorageMonitor storageMonitor,
            Phone phone, CellBroadcastHandler cellBroadcastHandler) {
            Phone phone) {
        super(name);

        mContext = context;
        mStorageMonitor = storageMonitor;
        mPhone = phone;
        mCellBroadcastHandler = cellBroadcastHandler;
        mResolver = context.getContentResolver();
        mWapPush = new WapPushOverSms(context);

@@ -1718,9 +1712,6 @@ public abstract class InboundSmsHandler extends StateMachine {
    @Override
    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        super.dump(fd, pw, args);
        if (mCellBroadcastHandler != null) {
            mCellBroadcastHandler.dump(fd, pw, args);
        }
        if (mCellBroadcastServiceManager != null) {
            mCellBroadcastServiceManager.dump(fd, pw, args);
        }
@@ -1807,15 +1798,11 @@ public abstract class InboundSmsHandler extends StateMachine {
    protected abstract class CbTestBroadcastReceiver extends BroadcastReceiver {

        protected abstract void handleTestAction(Intent intent);
        protected abstract void handleToggleEnable();
        protected abstract void handleToggleDisable(Context context);

        protected final String mTestAction;
        protected final String mToggleAction;

        public CbTestBroadcastReceiver(String testAction, String toggleAction) {
        public CbTestBroadcastReceiver(String testAction) {
            mTestAction = testAction;
            mToggleAction = toggleAction;
        }

        @Override
@@ -1829,19 +1816,6 @@ public abstract class InboundSmsHandler extends StateMachine {
                    return;
                }
                handleTestAction(intent);
            } else if (intent.getAction().equals(mToggleAction)) {
                if (intent.hasExtra("enable")) {
                    sEnableCbModule = intent.getBooleanExtra("enable", false);
                } else {
                    sEnableCbModule = !sEnableCbModule;
                }
                if (sEnableCbModule) {
                    log("enabling CB module");
                    handleToggleEnable();
                } else {
                    log("enabling legacy platform CB handling");
                    handleToggleDisable(context);
                }
            }
        }
    }
+6 −76

File changed.

Preview size limit exceeded, changes collapsed.

+0 −198

File deleted.

Preview size limit exceeded, changes collapsed.

+0 −380

File deleted.

Preview size limit exceeded, changes collapsed.

Loading