Add support for async requestCellInfoUpdate

-Add support for asynchronous CellInfo update requests.
-Return cached CellInfo for Q+ callers of
 getAllCellInfo().
-Naming cleanup to differentiate between
 getAllCellInfo() and requestCellInfoUpdate().

Bug: 37100068
Bug: 63737292
Bug: 26569588
Test: manual (via SL4A)
Change-Id: I85f063d54c314e38e1d9521847f5c2c079eb3aeb
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 5401ddc..740a0d4 100755
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -60,6 +60,7 @@
 import android.telephony.CellInfoWcdma;
 import android.telephony.CellLocation;
 import android.telephony.ClientRequestStats;
+import android.telephony.ICellInfoCallback;
 import android.telephony.IccOpenLogicalChannelResponse;
 import android.telephony.LocationAccessPolicy;
 import android.telephony.ModemActivityInfo;
@@ -217,6 +218,8 @@
     private static final int EVENT_GET_CELL_LOCATION_DONE = 63;
     private static final int CMD_MODEM_REBOOT = 64;
     private static final int EVENT_CMD_MODEM_REBOOT_DONE = 65;
+    private static final int CMD_REQUEST_CELL_INFO_UPDATE = 66;
+    private static final int EVENT_REQUEST_CELL_INFO_UPDATE_DONE = 67;
 
     // Parameters of select command.
     private static final int SELECT_COMMAND = 0xA4;
@@ -1000,13 +1003,11 @@
                     request.result = ar.exception == null;
                     notifyRequester(request);
                     break;
-
                 case CMD_GET_ALL_CELL_INFO:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_GET_ALL_CELL_INFO_DONE, request);
                     request.phone.requestCellInfoUpdate(request.workSource, onCompleted);
                     break;
-
                 case EVENT_GET_ALL_CELL_INFO_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
@@ -1017,22 +1018,45 @@
                         request.notifyAll();
                     }
                     break;
-
-                case CMD_GET_CELL_LOCATION: {
+                case CMD_REQUEST_CELL_INFO_UPDATE:
+                    request = (MainThreadRequest) msg.obj;
+                    request.phone.requestCellInfoUpdate(request.workSource,
+                            obtainMessage(EVENT_REQUEST_CELL_INFO_UPDATE_DONE, request));
+                    break;
+                case EVENT_REQUEST_CELL_INFO_UPDATE_DONE:
+                    ar = (AsyncResult) msg.obj;
+                    request = (MainThreadRequest) ar.userObj;
+                    ICellInfoCallback cb = (ICellInfoCallback) request.argument;
+                    try {
+                        if (ar.exception != null) {
+                            // something went wrong... the response is null
+                            Log.e(LOG_TAG, "Exception retrieving CellInfo=" + ar.exception);
+                            cb.onCellInfo(null);
+                        } else if (ar.result == null) {
+                            // timeout occurred, so force the result to non-null "empty"
+                            Log.w(LOG_TAG, "Timeout Waiting for CellInfo!");
+                            cb.onCellInfo(new ArrayList<CellInfo>());
+                        } else {
+                            // use the result as returned
+                            cb.onCellInfo((List<CellInfo>) ar.result);
+                        }
+                    } catch (RemoteException re) {
+                        Log.w(LOG_TAG, "Discarded CellInfo due to Callback RemoteException");
+                    }
+                    break;
+                case CMD_GET_CELL_LOCATION:
                     request = (MainThreadRequest) msg.obj;
                     WorkSource ws = (WorkSource) request.argument;
                     Phone phone = getPhoneFromRequest(request);
                     phone.getCellLocation(ws, obtainMessage(EVENT_GET_CELL_LOCATION_DONE, request));
                     break;
-                }
-
-                case EVENT_GET_CELL_LOCATION_DONE: {
+                case EVENT_GET_CELL_LOCATION_DONE:
                     ar = (AsyncResult) msg.obj;
                     request = (MainThreadRequest) ar.userObj;
                     if (ar.exception == null) {
                         request.result = ar.result;
                     } else {
-                        Phone phone = getPhoneFromRequest(request);
+                        phone = getPhoneFromRequest(request);
                         request.result = (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA)
                                 ? new CdmaCellLocation() : new GsmCellLocation();
                     }
@@ -1041,18 +1065,14 @@
                         request.notifyAll();
                     }
                     break;
-                }
-
                 case CMD_MODEM_REBOOT:
                     request = (MainThreadRequest) msg.obj;
                     onCompleted = obtainMessage(EVENT_RESET_MODEM_CONFIG_DONE, request);
                     mPhone.rebootModem(onCompleted);
                     break;
-
                 case EVENT_CMD_MODEM_REBOOT_DONE:
                     handleNullReturnEvent(msg, "rebootModem");
                     break;
-
                 default:
                     Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
                     break;
@@ -1178,10 +1198,19 @@
 
     /**
      * Same as {@link #sendRequestAsync(int)} except it takes an argument.
-     * @see {@link #sendRequest(int,Object)}
+     * @see {@link #sendRequest(int)}
      */
     private void sendRequestAsync(int command, Object argument) {
-        MainThreadRequest request = new MainThreadRequest(argument);
+        sendRequestAsync(command, argument, null, null);
+    }
+
+    /**
+     * Same as {@link #sendRequestAsync(int,Object)} except it takes a Phone and WorkSource.
+     * @see {@link #sendRequest(int,Object)}
+     */
+    private void sendRequestAsync(
+            int command, Object argument, Phone phone, WorkSource workSource) {
+        MainThreadRequest request = new MainThreadRequest(argument, phone, workSource);
         Message msg = mMainThreadHandler.obtainMessage(command, request);
         msg.sendToTarget();
     }
@@ -1915,6 +1944,14 @@
         return (neighbors.size()) > 0 ? neighbors : null;
     }
 
+    private List<CellInfo> getCachedCellInfo() {
+        List<CellInfo> cellInfos = new ArrayList<CellInfo>();
+        for (Phone phone : PhoneFactory.getPhones()) {
+            List<CellInfo> info = phone.getAllCellInfo();
+            if (info != null) cellInfos.addAll(info);
+        }
+        return cellInfos;
+    }
 
     @Override
     public List<CellInfo> getAllCellInfo(String callingPackage) {
@@ -1925,6 +1962,11 @@
             return null;
         }
 
+        final int targetSdk = getTargetSdk(callingPackage);
+        if (targetSdk >= android.os.Build.VERSION_CODES.Q) {
+            return getCachedCellInfo();
+        }
+
         if (DBG_LOC) log("getAllCellInfo: is active user");
         WorkSource workSource = getWorkSource(Binder.getCallingUid());
         final long identity = Binder.clearCallingIdentity();
@@ -1942,6 +1984,34 @@
     }
 
     @Override
+    public void requestCellInfoUpdate(int subId, ICellInfoCallback cb, String callingPackage) {
+        requestCellInfoUpdateInternal(
+                subId, cb, callingPackage, getWorkSource(Binder.getCallingUid()));
+    }
+
+    @Override
+    public void requestCellInfoUpdateWithWorkSource(
+            int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
+        enforceModifyPermission();
+        requestCellInfoUpdateInternal(subId, cb, callingPackage, workSource);
+    }
+
+    private void requestCellInfoUpdateInternal(
+            int subId, ICellInfoCallback cb, String callingPackage, WorkSource workSource) {
+        mPhone.getContext().getSystemService(AppOpsManager.class)
+                .checkPackage(Binder.getCallingUid(), callingPackage);
+        if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
+                callingPackage, Binder.getCallingUid(), Binder.getCallingPid(), true)) {
+            return;
+        }
+
+        final Phone phone = getPhone(subId);
+        if (phone == null) throw new IllegalArgumentException("Invalid Subscription Id: " + subId);
+
+        sendRequestAsync(CMD_REQUEST_CELL_INFO_UPDATE, cb, phone, workSource);
+    }
+
+    @Override
     public void setCellInfoListRate(int rateInMillis) {
         enforceModifyPermission();
         WorkSource workSource = getWorkSource(Binder.getCallingUid());