Merge "Add CDMA SCP message handling"
diff --git a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
index 13dea00..ffbef89 100644
--- a/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
+++ b/src/com/android/cellbroadcastservice/CellBroadcastHandler.java
@@ -16,6 +16,8 @@
 
 package com.android.cellbroadcastservice;
 
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
 import static android.content.PermissionChecker.PERMISSION_GRANTED;
 import static android.provider.Settings.Secure.CMAS_ADDITIONAL_BROADCAST_PKG;
 
@@ -29,13 +31,12 @@
 import android.content.Intent;
 import android.content.PermissionChecker;
 import android.location.Location;
-import android.location.LocationListener;
 import android.location.LocationManager;
+import android.location.LocationRequest;
 import android.net.Uri;
 import android.os.Build;
-import android.os.Bundle;
 import android.os.Handler;
-import android.os.Looper;
+import android.os.HandlerExecutor;
 import android.os.Message;
 import android.os.UserHandle;
 import android.provider.Settings;
@@ -43,7 +44,6 @@
 import android.provider.Telephony.CellBroadcasts;
 import android.telephony.SmsCbMessage;
 import android.telephony.SubscriptionManager;
-import android.text.format.DateUtils;
 import android.util.LocalLog;
 import android.util.Log;
 
@@ -56,6 +56,7 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Dispatch new Cell Broadcasts to receivers. Acquires a private wakelock until the broadcast
@@ -66,10 +67,8 @@
 
     private final LocalLog mLocalLog = new LocalLog(100);
 
-    protected static final Uri CELL_BROADCAST_URI = Uri.parse("content://cellbroadcasts_fwk");
-
     /** Uses to request the location update. */
-    public final LocationRequester mLocationRequester;
+    private final LocationRequester mLocationRequester;
 
     private CellBroadcastHandler(Context context) {
         this("CellBroadcastHandler", context);
@@ -80,7 +79,7 @@
         mLocationRequester = new LocationRequester(
                 context,
                 (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE),
-                getHandler().getLooper());
+                getHandler());
     }
 
     /**
@@ -128,7 +127,7 @@
         // 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);
+        Uri uri = mContext.getContentResolver().insert(CellBroadcasts.CONTENT_URI, cv);
 
         if (message.needGeoFencingCheck()) {
             if (DBG) {
@@ -263,7 +262,7 @@
         if (messageUri != null) {
             ContentValues cv = new ContentValues();
             cv.put(CellBroadcasts.MESSAGE_BROADCASTED, 1);
-            mContext.getContentResolver().update(CELL_BROADCAST_URI, cv,
+            mContext.getContentResolver().update(CellBroadcasts.CONTENT_URI, cv,
                     CellBroadcasts._ID + "=?", new String[] {messageUri.getLastPathSegment()});
         }
     }
@@ -289,21 +288,6 @@
         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.
          */
@@ -311,17 +295,18 @@
                 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;
+        private final Handler mLocationHandler;
 
-        LocationRequester(Context context, LocationManager locationManager, Looper looper) {
+        private boolean mLocationUpdateInProgress;
+
+        LocationRequester(Context context, LocationManager locationManager, Handler handler) {
             mLocationManager = locationManager;
-            mLooper = looper;
             mCallbacks = new ArrayList<>();
             mContext = context;
-            mLocationHandler = new LocationHandler(looper);
+            mLocationHandler = handler;
+            mLocationUpdateInProgress = false;
         }
 
         /**
@@ -329,103 +314,71 @@
          * {@code null} location will be called immediately.
          *
          * @param callback a callback to the response when the location is available
-         * @param maximumWaitTimeSec the maximum wait time of this request. If location is not
+         * @param maximumWaitTimeS 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();
+                int maximumWaitTimeS) {
+            mLocationHandler.post(() -> requestLocationUpdateInternal(callback, maximumWaitTimeS));
         }
 
-        private void onLocationUpdate(@Nullable LatLng location) {
+        private void onLocationUpdate(@Nullable Location location) {
+            if (DBG) {
+                Log.d(TAG, "no location available");
+            }
+
+            mLocationUpdateInProgress = false;
             for (LocationUpdateCallback callback : mCallbacks) {
-                callback.onLocationUpdate(location);
+                if (location != null) {
+                    callback.onLocationUpdate(
+                            new LatLng(location.getLatitude(), location.getLongitude()));
+                } else {
+                    callback.onLocationUpdate(null);
+                }
             }
             mCallbacks.clear();
         }
 
         private void requestLocationUpdateInternal(@NonNull LocationUpdateCallback callback,
-                int maximumWaitTimeSec) {
+                int maximumWaitTimeS) {
             if (DBG) Log.d(TAG, "requestLocationUpdate");
-            if (!isLocationServiceAvailable()) {
+            if (!hasPermission(ACCESS_FINE_LOCATION) && !hasPermission(ACCESS_COARSE_LOCATION)) {
                 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);
+            if (!mLocationUpdateInProgress) {
+                for (String provider : LOCATION_PROVIDERS) {
+                    if (!mLocationManager.isProviderEnabled(provider)) {
+                        if (DBG) {
+                            Log.d(TAG, "provider " + provider + " not available");
+                        }
+                        continue;
+                    }
+                    LocationRequest request = LocationRequest.createFromDeprecatedProvider(provider,
+                            0, 0, true);
+                    if (maximumWaitTimeS != SmsCbMessage.MAXIMUM_WAIT_TIME_NOT_SET) {
+                        request.setExpireIn(TimeUnit.SECONDS.toMillis(maximumWaitTimeS));
+                    }
+                    mLocationManager.getCurrentLocation(request, null,
+                            new HandlerExecutor(mLocationHandler), this::onLocationUpdate);
+                    mLocationUpdateInProgress = true;
                     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;
+            if (mLocationUpdateInProgress) {
+                mCallbacks.add(callback);
+            } else {
+                callback.onLocationUpdate(null);
             }
-            return false;
         }
 
         private boolean hasPermission(String permission) {
             return PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(mContext,
                     permission, null) == 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);
-                }
-            }
-        }
     }
 }
diff --git a/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java b/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
index 7a95397..b9b996c 100644
--- a/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
+++ b/src/com/android/cellbroadcastservice/GsmCellBroadcastHandler.java
@@ -110,7 +110,7 @@
 
         ContentResolver resolver = mContext.getContentResolver();
         for (CellBroadcastIdentity identity : geoFencingTriggerMessage.cbIdentifiers) {
-            try (Cursor cursor = resolver.query(CELL_BROADCAST_URI,
+            try (Cursor cursor = resolver.query(CellBroadcasts.CONTENT_URI,
                     CellBroadcasts.QUERY_COLUMNS_FWK,
                     where,
                     new String[] { Integer.toString(identity.messageIdentifier),
@@ -120,7 +120,7 @@
                 if (cursor != null) {
                     while (cursor.moveToNext()) {
                         cbMessages.add(SmsCbMessage.createFromCursor(cursor));
-                        cbMessageUris.add(ContentUris.withAppendedId(CELL_BROADCAST_URI,
+                        cbMessageUris.add(ContentUris.withAppendedId(CellBroadcasts.CONTENT_URI,
                                 cursor.getInt(cursor.getColumnIndex(CellBroadcasts._ID))));
                     }
                 }