Add API to cancel an active weather request

Add new API cancelRequest to CMWeatherManager. This will allow
clients to cancel a request that was previuosly submitted to
the weather service.

As part of this change, requestWeatherUpdate(weatherLocation),
requestWeatherUpdate(Location) and lookupCity(cityName) will
now return the RequestInfo object created if the request
was successfully submitted to the weather manager service

TICKET: CYNGNOS-2383
TICKET: CYNGNOS-2385

Change-Id: Ic122f91e0ea8a24d81dbed48741ef1e33567b56c
diff --git a/api/cm_current.txt b/api/cm_current.txt
index 758315e..a00cf44 100644
--- a/api/cm_current.txt
+++ b/api/cm_current.txt
@@ -1299,12 +1299,13 @@
 package cyanogenmod.weather {
 
   public class CMWeatherManager {
+    method public void cancelRequest(cyanogenmod.weather.RequestInfo);
     method public java.lang.String getActiveWeatherServiceProviderLabel();
     method public static cyanogenmod.weather.CMWeatherManager getInstance(android.content.Context);
-    method public void lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
+    method public cyanogenmod.weather.RequestInfo lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
     method public void registerWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
-    method public void requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
-    method public void requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
+    method public cyanogenmod.weather.RequestInfo requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
+    method public cyanogenmod.weather.RequestInfo requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
     method public void unregisterWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
     field public static final int WEATHER_REQUEST_ALREADY_IN_PROGRESS = -3; // 0xfffffffd
     field public static final int WEATHER_REQUEST_COMPLETED = 1; // 0x1
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/CMWeatherManagerService.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/CMWeatherManagerService.java
index faab108..d34454e 100644
--- a/cm/lib/main/java/org/cyanogenmod/platform/internal/CMWeatherManagerService.java
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/CMWeatherManagerService.java
@@ -220,6 +220,12 @@
             }
             return null;
         }
+
+        @Override
+        public void cancelRequest(RequestInfo info) {
+            enforcePermission();
+            processCancelRequest(info);
+        }
     };
 
     private String getComponentLabel(ComponentName componentName) {
@@ -334,6 +340,15 @@
         }
     }
 
+    private void processCancelRequest(RequestInfo info) {
+        if (mIsWeatherProviderServiceBound) {
+            try {
+                mWeatherProviderService.cancelRequest(info);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
     private ServiceConnection mWeatherServiceProviderConnection = new ServiceConnection() {
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
diff --git a/sdk/src/java/cyanogenmod/weather/CMWeatherManager.java b/sdk/src/java/cyanogenmod/weather/CMWeatherManager.java
index 32eab52..3ab510e 100644
--- a/sdk/src/java/cyanogenmod/weather/CMWeatherManager.java
+++ b/sdk/src/java/cyanogenmod/weather/CMWeatherManager.java
@@ -131,11 +131,14 @@
      * @param listener {@link WeatherUpdateRequestListener} To be notified once the active weather
      *                                                     service provider has finished
      *                                                     processing your request
+     * @return A {@link RequestInfo} identifying the request submitted to the weather service.
+     * Note that this method might return null if an error occurred while trying to submit
+     * the request.
      */
-    public void requestWeatherUpdate(@NonNull Location location,
+    public RequestInfo requestWeatherUpdate(@NonNull Location location,
             @NonNull WeatherUpdateRequestListener listener) {
         if (sWeatherManagerService == null) {
-            return;
+            return null;
         }
 
         try {
@@ -145,7 +148,9 @@
                     .build();
             if (listener != null) mWeatherUpdateRequestListeners.put(info, listener);
             sWeatherManagerService.updateWeather(info);
+            return info;
         } catch (RemoteException e) {
+            return null;
         }
     }
 
@@ -159,11 +164,14 @@
      * @param listener {@link WeatherUpdateRequestListener} To be notified once the active weather
      *                                                     service provider has finished
      *                                                     processing your request
+     * @return A {@link RequestInfo} identifying the request submitted to the weather service.
+     * Note that this method might return null if an error occurred while trying to submit
+     * the request.
      */
-    public void requestWeatherUpdate(@NonNull WeatherLocation weatherLocation,
+    public RequestInfo requestWeatherUpdate(@NonNull WeatherLocation weatherLocation,
             @NonNull WeatherUpdateRequestListener listener) {
         if (sWeatherManagerService == null) {
-            return;
+            return null;
         }
 
         try {
@@ -173,7 +181,9 @@
                     .build();
             if (listener != null) mWeatherUpdateRequestListeners.put(info, listener);
             sWeatherManagerService.updateWeather(info);
+            return info;
         } catch (RemoteException e) {
+            return null;
         }
     }
 
@@ -185,10 +195,13 @@
      *                                                  completed. Upon success, a list of
      *                                                  {@link cyanogenmod.weather.WeatherLocation}
      *                                                  will be provided
+     * @return A {@link RequestInfo} identifying the request submitted to the weather service.
+     * Note that this method might return null if an error occurred while trying to submit
+     * the request.
      */
-    public void lookupCity(@NonNull String city, @NonNull LookupCityRequestListener listener) {
+    public RequestInfo lookupCity(@NonNull String city, @NonNull LookupCityRequestListener listener) {
         if (sWeatherManagerService == null) {
-            return;
+            return null;
         }
         try {
             RequestInfo info = new RequestInfo
@@ -197,7 +210,27 @@
                     .build();
             if (listener != null) mLookupNameRequestListeners.put(info, listener);
             sWeatherManagerService.lookupCity(info);
+            return info;
         } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Cancels a request that was previously submitted to the weather service.
+     * @param info The {@link RequestInfo} that you received when the request was submitted
+     */
+    public void cancelRequest(RequestInfo info) {
+        if (sWeatherManagerService == null) {
+            return;
+        }
+        if (info == null) {
+            return;
+        }
+
+        try {
+            sWeatherManagerService.cancelRequest(info);
+        }catch (RemoteException e){
         }
     }
 
diff --git a/sdk/src/java/cyanogenmod/weather/ICMWeatherManager.aidl b/sdk/src/java/cyanogenmod/weather/ICMWeatherManager.aidl
index 21e1d26..8caac49 100644
--- a/sdk/src/java/cyanogenmod/weather/ICMWeatherManager.aidl
+++ b/sdk/src/java/cyanogenmod/weather/ICMWeatherManager.aidl
@@ -28,4 +28,5 @@
     oneway void unregisterWeatherServiceProviderChangeListener(
         in IWeatherServiceProviderChangeListener listener);
     String getActiveWeatherServiceProviderLabel();
+    oneway void cancelRequest(in RequestInfo info);
 }
\ No newline at end of file
diff --git a/sdk/src/java/cyanogenmod/weather/RequestInfo.java b/sdk/src/java/cyanogenmod/weather/RequestInfo.java
index 353b5fd..4086c62 100644
--- a/sdk/src/java/cyanogenmod/weather/RequestInfo.java
+++ b/sdk/src/java/cyanogenmod/weather/RequestInfo.java
@@ -85,7 +85,7 @@
          * set, will null out the city name and weather location.
          */
         public Builder setLocation(Location location) {
-            this.mLocation = location;
+            this.mLocation = new Location(location);
             this.mCityName = null;
             this.mWeatherLocation = null;
             this.mRequestType = TYPE_GEO_LOCATION_REQ;
@@ -208,7 +208,7 @@
      * otherwise
      */
     public Location getLocation() {
-        return mLocation;
+        return new Location(mLocation);
     }
 
     /**
diff --git a/sdk/src/java/cyanogenmod/weatherservice/IWeatherProviderService.aidl b/sdk/src/java/cyanogenmod/weatherservice/IWeatherProviderService.aidl
index d9eceb3..2a1deb5 100644
--- a/sdk/src/java/cyanogenmod/weatherservice/IWeatherProviderService.aidl
+++ b/sdk/src/java/cyanogenmod/weatherservice/IWeatherProviderService.aidl
@@ -25,4 +25,5 @@
     void processCityNameLookupRequest(in RequestInfo request);
     void setServiceClient(in IWeatherProviderServiceClient client);
     void cancelOngoingRequests();
+    void cancelRequest(in RequestInfo request);
 }
\ No newline at end of file
diff --git a/sdk/src/java/cyanogenmod/weatherservice/WeatherProviderService.java b/sdk/src/java/cyanogenmod/weatherservice/WeatherProviderService.java
index 759d5fc..8a1f30c 100644
--- a/sdk/src/java/cyanogenmod/weatherservice/WeatherProviderService.java
+++ b/sdk/src/java/cyanogenmod/weatherservice/WeatherProviderService.java
@@ -35,8 +35,8 @@
  * can handle weather update requests and update the weather content provider data by processing
  * a {@link ServiceRequest}
  *
- * A print service is declared as any other service in an AndroidManifest.xml but it must also
- * specify that in handles the {@link android.content.Intent} with action
+ * A weather provider service is declared as any other service in an AndroidManifest.xml but it must
+ * also specify that in handles the {@link android.content.Intent} with action
  * {@link #SERVICE_INTERFACE cyanogenmod.weatherservice.WeatherProviderService}. Failure to declare
  * this intent will cause the system to ignore the weather provider service. Additionally, a
  * weather provider service must request the
@@ -108,7 +108,13 @@
 
         @Override
         public void cancelOngoingRequests() {
-            mHandler.obtainMessage(ServiceHandler.MSG_CANCEL_ONGOING_REQUESTS).sendToTarget();
+            mHandler.obtainMessage(ServiceHandler.MSG_CANCEL_ALL_OUTSTANDING_REQUESTS)
+                    .sendToTarget();
+        }
+
+        @Override
+        public void cancelRequest(RequestInfo info) {
+            mHandler.obtainMessage(ServiceHandler.MSG_CANCEL_REQUEST, info).sendToTarget();
         }
     };
 
@@ -119,7 +125,8 @@
         }
         public static final int MSG_SET_CLIENT = 1;
         public static final int MSG_ON_NEW_REQUEST = 2;
-        public static final int MSG_CANCEL_ONGOING_REQUESTS = 3;
+        public static final int MSG_CANCEL_ALL_OUTSTANDING_REQUESTS = 3;
+        public static final int MSG_CANCEL_REQUEST = 4;
 
         @Override
         public void handleMessage(Message msg) {
@@ -144,7 +151,7 @@
                     }
                     return;
                 }
-                case MSG_CANCEL_ONGOING_REQUESTS: {
+                case MSG_CANCEL_ALL_OUTSTANDING_REQUESTS: {
                     synchronized (mWeakRequestsSet) {
                         for (final ServiceRequest request : mWeakRequestsSet) {
                             if (request != null) {
@@ -159,6 +166,27 @@
                             }
                         }
                     }
+                    return;
+                }
+                case MSG_CANCEL_REQUEST: {
+                    synchronized (mWeakRequestsSet) {
+                        RequestInfo info = (RequestInfo) msg.obj;
+                        if (info == null) return;
+                        for (final ServiceRequest request : mWeakRequestsSet) {
+                            if (request.getRequestInfo().equals(info)) {
+                                request.cancel();
+                                mWeakRequestsSet.remove(request);
+                                mHandler.post(new Runnable() {
+                                    @Override
+                                    public void run() {
+                                        onRequestCancelled(request);
+                                    }
+                                });
+                                break;
+                            }
+                        }
+                    }
+                    return;
                 }
             }
         }
diff --git a/system-api/cm_system-current.txt b/system-api/cm_system-current.txt
index 758315e..a00cf44 100644
--- a/system-api/cm_system-current.txt
+++ b/system-api/cm_system-current.txt
@@ -1299,12 +1299,13 @@
 package cyanogenmod.weather {
 
   public class CMWeatherManager {
+    method public void cancelRequest(cyanogenmod.weather.RequestInfo);
     method public java.lang.String getActiveWeatherServiceProviderLabel();
     method public static cyanogenmod.weather.CMWeatherManager getInstance(android.content.Context);
-    method public void lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
+    method public cyanogenmod.weather.RequestInfo lookupCity(java.lang.String, cyanogenmod.weather.CMWeatherManager.LookupCityRequestListener);
     method public void registerWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
-    method public void requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
-    method public void requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
+    method public cyanogenmod.weather.RequestInfo requestWeatherUpdate(android.location.Location, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
+    method public cyanogenmod.weather.RequestInfo requestWeatherUpdate(cyanogenmod.weather.WeatherLocation, cyanogenmod.weather.CMWeatherManager.WeatherUpdateRequestListener);
     method public void unregisterWeatherServiceProviderChangeListener(cyanogenmod.weather.CMWeatherManager.WeatherServiceProviderChangeListener);
     field public static final int WEATHER_REQUEST_ALREADY_IN_PROGRESS = -3; // 0xfffffffd
     field public static final int WEATHER_REQUEST_COMPLETED = 1; // 0x1