Modify testapps to exercise ACTION_CLEANUP

Change-Id: Ie980c5554663bb3ed132920947aa22366f0d47e2
diff --git a/testapps/EmbmsServiceTestApp/AndroidManifest.xml b/testapps/EmbmsServiceTestApp/AndroidManifest.xml
index 526d3af..edcb5cb 100644
--- a/testapps/EmbmsServiceTestApp/AndroidManifest.xml
+++ b/testapps/EmbmsServiceTestApp/AndroidManifest.xml
@@ -33,6 +33,10 @@
         <action android:name="android.telephony.action.EmbmsDownload" />
       </intent-filter>
     </service>
+
+    <receiver android:name="com.android.phone.testapps.embmsmw.SideChannelReceiver"
+              android:enabled="true"
+              android:exported="true"/>
   </application>
 </manifest>
 
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
index f7abeb9..67083d7 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
@@ -47,6 +47,7 @@
 import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -152,29 +153,98 @@
         }
     };
 
+    private static EmbmsSampleDownloadService sInstance = null;
+
     private final Map<FrontendAppIdentifier, IMbmsDownloadManagerCallback> mAppCallbacks =
             new HashMap<>();
     private final Map<FrontendAppIdentifier, ComponentName> mAppReceivers = new HashMap<>();
     private final Map<FrontendAppIdentifier, String> mAppTempFileRoots = new HashMap<>();
     private final Map<FrontendAppIdentifier, Boolean> mDoesAppHaveActiveDownload =
             new ConcurrentHashMap<>();
+    // A map of app-identifiers to (maps of service-ids to sets of temp file uris in use)
+    private final Map<FrontendAppIdentifier, Map<String, Set<Uri>>> mTempFilesInUse =
+            new ConcurrentHashMap<>();
 
     private HandlerThread mHandlerThread;
     private Handler mHandler;
+    private int mDownloadDelayFactor = 1;
 
     @Override
     public IBinder onBind(Intent intent) {
         mHandlerThread = new HandlerThread("EmbmsTestDownloadServiceWorker");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+        sInstance = this;
         return mBinder.asBinder();
     }
 
+    public static EmbmsSampleDownloadService getInstance() {
+        return sInstance;
+    }
+
+    public void requestCleanup() {
+        // Assume that there's only one app, and do it for all the services.
+        FrontendAppIdentifier registeredAppId = mAppReceivers.keySet().iterator().next();
+        ComponentName appReceiver = mAppReceivers.values().iterator().next();
+        for (FileServiceInfo fileServiceInfo :
+                FileServiceRepository.getInstance(this).getAllFileServices()) {
+            Intent cleanupIntent = new Intent(MbmsDownloadManager.ACTION_CLEANUP);
+            cleanupIntent.setComponent(appReceiver);
+            cleanupIntent.putExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO, fileServiceInfo);
+            cleanupIntent.putExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT,
+                    mAppTempFileRoots.get(registeredAppId));
+            Set<Uri> tempFilesInUse =
+                    mTempFilesInUse.getOrDefault(registeredAppId, Collections.emptyMap())
+                            .getOrDefault(fileServiceInfo.getServiceId(), Collections.emptySet());
+            cleanupIntent.putExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE,
+                    new ArrayList<>(tempFilesInUse));
+            sendBroadcast(cleanupIntent);
+        }
+    }
+
+    public void requestExtraTempFiles(FileServiceInfo serviceInfo) {
+        // Assume one app, and do it for the specified service.
+        FrontendAppIdentifier registeredAppId = mAppReceivers.keySet().iterator().next();
+        ComponentName appReceiver = mAppReceivers.values().iterator().next();
+        Intent fdRequestIntent = new Intent(MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST);
+        fdRequestIntent.putExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO, serviceInfo);
+        fdRequestIntent.putExtra(MbmsDownloadManager.EXTRA_FD_COUNT, 10);
+        fdRequestIntent.putExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT,
+                mAppTempFileRoots.get(registeredAppId));
+        fdRequestIntent.setComponent(appReceiver);
+
+        sendOrderedBroadcast(fdRequestIntent,
+                null, // receiverPermission
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        int result = getResultCode();
+                        Bundle extras = getResultExtras(false);
+                        Log.i(LOG_TAG, "Received extra temp files. Result " + result);
+                        if (extras != null) {
+                            Log.i(LOG_TAG, "Got "
+                                    + extras.getParcelableArrayList(
+                                    MbmsDownloadManager.EXTRA_FREE_URI_LIST).size()
+                                    + " fds");
+                        }
+                    }
+                },
+                null, // scheduler
+                Activity.RESULT_OK,
+                null, // initialData
+                null /* initialExtras */);
+    }
+
+    public void delayDownloads(int factor) {
+        mDownloadDelayFactor = factor;
+    }
+
     private void sendFdRequest(DownloadRequest request, FrontendAppIdentifier appKey) {
         int numFds = getNumFdsNeededForRequest(request);
         // Compose the FILE_DESCRIPTOR_REQUEST_INTENT
         Intent requestIntent = new Intent(MbmsDownloadManager.ACTION_FILE_DESCRIPTOR_REQUEST);
-        requestIntent.putExtra(MbmsDownloadManager.EXTRA_REQUEST, request);
+        requestIntent.putExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO,
+                request.getFileServiceInfo());
         requestIntent.putExtra(MbmsDownloadManager.EXTRA_FD_COUNT, numFds);
         requestIntent.putExtra(MbmsDownloadManager.EXTRA_TEMP_FILE_ROOT,
                 mAppTempFileRoots.get(appKey));
@@ -219,14 +289,18 @@
                 break;
             }
             UriPathPair tempFile = tempFiles.get(i);
+            addTempFileInUse(appKey, request.getFileServiceInfo().getServiceId(),
+                    tempFile.getFilePathUri());
             FileInfo fileToDownload = filesToDownload.get(i);
             final boolean isLastFile = i == tempFiles.size() - 1;
             mHandler.postDelayed(() -> {
                 downloadSingleFile(appKey, request, tempFile, fileToDownload);
+                removeTempFileInUse(appKey, request.getFileServiceInfo().getServiceId(),
+                        tempFile.getFilePathUri());
                 if (isLastFile) {
                     mDoesAppHaveActiveDownload.put(appKey, false);
                 }
-            }, FILE_SEPARATION_DELAY * i);
+            }, FILE_SEPARATION_DELAY * i * mDownloadDelayFactor);
         }
     }
 
@@ -296,4 +370,30 @@
     private int getNumFdsNeededForRequest(DownloadRequest request) {
         return request.getFileServiceInfo().getFiles().size();
     }
+
+    private void addTempFileInUse(FrontendAppIdentifier appKey, String serviceId, Uri tempFileUri) {
+        Map<String, Set<Uri>> tempFileByService = mTempFilesInUse.get(appKey);
+        if (tempFileByService == null) {
+            tempFileByService = new ConcurrentHashMap<>();
+            mTempFilesInUse.put(appKey, tempFileByService);
+        }
+        Set<Uri> tempFilesInUse = tempFileByService.get(serviceId);
+        if (tempFilesInUse == null) {
+            tempFilesInUse = ConcurrentHashMap.newKeySet();
+            tempFileByService.put(serviceId, tempFilesInUse);
+        }
+        tempFilesInUse.add(tempFileUri);
+    }
+
+    private void removeTempFileInUse(FrontendAppIdentifier appKey, String serviceId,
+            Uri tempFileUri) {
+        Set<Uri> tempFilesInUse = mTempFilesInUse.getOrDefault(appKey, Collections.emptyMap())
+                .getOrDefault(serviceId, Collections.emptySet());
+        if (tempFilesInUse.contains(tempFileUri)) {
+            tempFilesInUse.remove(tempFileUri);
+        } else {
+            Log.w(LOG_TAG, "Trying to remove unknown temp file in use " + tempFileUri + " for app" +
+                    appKey + " and service id " + serviceId);
+        }
+    }
 }
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/FileServiceRepository.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/FileServiceRepository.java
index 4d0b6c3..a771a1a 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/FileServiceRepository.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/FileServiceRepository.java
@@ -71,6 +71,10 @@
                 .collect(Collectors.toList());
     }
 
+    public List<FileServiceInfo> getAllFileServices() {
+        return new ArrayList<>(mIdToServiceInfo.values());
+    }
+
     public FileServiceInfo getFileServiceInfoForId(String serviceId) {
         return mIdToServiceInfo.getOrDefault(serviceId, null);
     }
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/SideChannelReceiver.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/SideChannelReceiver.java
new file mode 100644
index 0000000..c9d38a0
--- /dev/null
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/SideChannelReceiver.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2017 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.phone.testapps.embmsmw;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.telephony.mbms.FileServiceInfo;
+import android.util.Log;
+
+/**
+ * Class for triggering artificial events from the frontend app. These would normally not come
+ * from the frontend app in a real embms implementation.
+ */
+public class SideChannelReceiver extends BroadcastReceiver {
+    public static final String ACTION_TRIGGER_CLEANUP =
+            "com.android.phone.testapps.embmsmw.TRIGGER_CLEANUP";
+    public static final String ACTION_REQUEST_SPURIOUS_TEMP_FILES =
+            "com.android.phone.testapps.embmsmw.REQUEST_SPURIOUS_TEMP_FILES";
+    public static final String ACTION_DELAY_DOWNLOAD =
+            "com.android.phone.testapps.embmsmw.DELAY_DOWNLOAD";
+
+    public static final String EXTRA_SERVICE_INFO =
+            "com.android.phone.testapps.embmsmw.SERVICE_INFO";
+    public static final String EXTRA_DELAY_FACTOR =
+            "com.android.phone.testapps.embmsmw.DELAY_FACTOR";
+
+    private static final String LOG_TAG = "EmbmsSampleMwSC";
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        EmbmsSampleDownloadService downloadService = EmbmsSampleDownloadService.getInstance();
+        if (downloadService == null) {
+            Log.w(LOG_TAG, "don't have instance of dl service");
+            return;
+        }
+        switch (intent.getAction()) {
+            case ACTION_TRIGGER_CLEANUP:
+                downloadService.requestCleanup();
+                break;
+            case ACTION_REQUEST_SPURIOUS_TEMP_FILES:
+                FileServiceInfo serviceInfo = intent.getParcelableExtra(EXTRA_SERVICE_INFO);
+                downloadService.requestExtraTempFiles(serviceInfo);
+                break;
+            case ACTION_DELAY_DOWNLOAD:
+                // Increase download latency by a certain factor
+                downloadService.delayDownloads(intent.getIntExtra(EXTRA_DELAY_FACTOR, 1));
+                break;
+
+        }
+    }
+}