getStreamingServices for Embms

Add code in frontend app to call getStreamingServices.
Add code in backend to generate a list and return it after some delay.

Test: testapps
Change-Id: I4265149b29daa2fbbfc49196e264c194f896a417
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsTestStreamingService.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsTestStreamingService.java
index c0e601f..11f4e6d 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsTestStreamingService.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsTestStreamingService.java
@@ -16,27 +16,79 @@
 
 package com.android.phone.testapps.embmsmw;
 
+import android.app.AppOpsManager;
 import android.app.Service;
+import android.content.Context;
 import android.content.Intent;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.IBinder;
+import android.os.RemoteException;
 import android.telephony.mbms.IMbmsStreamingManagerCallback;
 import android.telephony.mbms.MbmsException;
+import android.telephony.mbms.StreamingServiceInfo;
 import android.telephony.mbms.vendor.IMbmsStreamingService;
 import android.telephony.mbms.vendor.MbmsStreamingServiceBase;
 import android.util.Log;
 
+import com.android.internal.os.SomeArgs;
+
+import java.util.Arrays;
+import java.util.Date;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 public class EmbmsTestStreamingService extends Service {
-    private final static String TAG = "EmbmsTestStreaming";
-    private final Map<String, IMbmsStreamingManagerCallback> mAppCallbacks = new HashMap<>();
+    private static final Set<String> ALLOWED_PACKAGES = new HashSet<String>() {{
+        add("com.android.phone.testapps.embmsfrontend");
+    }};
+
+    private static final String TAG = "EmbmsTestStreaming";
+    private static final int SEND_STREAMING_SERVICES_LIST = 1;
+
+    private final Map<StreamingAppIdentifier, IMbmsStreamingManagerCallback> mAppCallbacks =
+            new HashMap<>();
+
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private Handler.Callback mWorkerCallback = (msg) -> {
+        switch (msg.what) {
+            case SEND_STREAMING_SERVICES_LIST:
+                SomeArgs args = (SomeArgs) msg.obj;
+                StreamingAppIdentifier appKey = (StreamingAppIdentifier) args.arg1;
+                List<StreamingServiceInfo> services = (List) args.arg2;
+                IMbmsStreamingManagerCallback appCallback = mAppCallbacks.get(appKey);
+                if (appCallback != null) {
+                    try {
+                        appCallback.streamingServicesUpdated(services);
+                    } catch (RemoteException e) {
+                        // Assume app has gone away and clean up.
+                    }
+                }
+        }
+        return true;
+    };
 
     private final IMbmsStreamingService.Stub mBinder = new MbmsStreamingServiceBase() {
         @Override
-        public int initialize(IMbmsStreamingManagerCallback listener, String appName, int subId)
-                throws MbmsException {
-            String appKey = appName + subId;
+        public int initialize(IMbmsStreamingManagerCallback listener, String appName, int subId) {
+            String[] packageNames = getPackageManager().getPackagesForUid(Binder.getCallingUid());
+            if (packageNames == null) {
+                return MbmsException.ERROR_APP_PERMISSIONS_NOT_GRANTED;
+            }
+            boolean isUidAllowed = Arrays.stream(packageNames).anyMatch(ALLOWED_PACKAGES::contains);
+            if (!isUidAllowed) {
+                return MbmsException.ERROR_APP_PERMISSIONS_NOT_GRANTED;
+            }
+
+            StreamingAppIdentifier appKey =
+                    new StreamingAppIdentifier(Binder.getCallingUid(), appName, subId);
             if (!mAppCallbacks.containsKey(appKey)) {
                 mAppCallbacks.put(appKey, listener);
             } else {
@@ -44,22 +96,57 @@
             }
             return 0;
         }
-    };
 
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        logd("EmbmsTestStreamingService onCreate");
-    }
+        @Override
+        public int getStreamingServices(String appName, int subscriptionId,
+                List<String> serviceClasses) {
+            StreamingAppIdentifier appKey =
+                    new StreamingAppIdentifier(Binder.getCallingUid(), appName, subscriptionId);
+            if (!mAppCallbacks.containsKey(appKey)) {
+                return MbmsException.ERROR_NOT_YET_INITIALIZED;
+            }
+
+            Map<Locale, String> nameDict1 = new HashMap<Locale, String>() {{
+                put(Locale.US, "TestService1");
+            }};
+            Map<Locale, String> nameDict2 = new HashMap<Locale, String>() {{
+                put(Locale.US, "TestService1");
+            }};
+            StreamingServiceInfo info1 = new StreamingServiceInfo(nameDict1, "Class 1", Locale.US,
+                    "Service ID 1", new Date(System.currentTimeMillis() - 10000),
+                    new Date(System.currentTimeMillis() + 10000));
+            StreamingServiceInfo info2 = new StreamingServiceInfo(nameDict2, "Class 2", Locale.US,
+                    "Service ID 2", new Date(System.currentTimeMillis() - 20000),
+                    new Date(System.currentTimeMillis() + 20000));
+            List<StreamingServiceInfo> serviceInfos = new LinkedList<StreamingServiceInfo>() {{
+                add(info1);
+                add(info2);
+            }};
+
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = appKey;
+            args.arg2 = serviceInfos;
+
+            mHandler.removeMessages(SEND_STREAMING_SERVICES_LIST);
+            mHandler.sendMessageDelayed(
+                    mHandler.obtainMessage(SEND_STREAMING_SERVICES_LIST, args), 300);
+            return MbmsException.SUCCESS;
+        }
+    };
 
     @Override
     public void onDestroy() {
         super.onCreate();
+        mHandlerThread.quitSafely();
         logd("EmbmsTestStreamingService onDestroy");
     }
 
     @Override
     public IBinder onBind(Intent intent) {
+        logd("EmbmsTestStreamingService onBind");
+        mHandlerThread = new HandlerThread("EmbmsTestStreamingServiceWorker");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper(), mWorkerCallback);
         return mBinder;
     }
 
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/StreamingAppIdentifier.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/StreamingAppIdentifier.java
new file mode 100644
index 0000000..7cbb14a
--- /dev/null
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/StreamingAppIdentifier.java
@@ -0,0 +1,69 @@
+/*
+ * 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;
+
+public class StreamingAppIdentifier {
+    private final int uid;
+    private final String appName;
+    private final int subscriptionId;
+
+    public StreamingAppIdentifier(int uid, String appName, int subscriptionId) {
+        this.uid = uid;
+        this.appName = appName;
+        this.subscriptionId = subscriptionId;
+    }
+
+    public int getUid() {
+        return uid;
+    }
+
+    public String getAppName() {
+        return appName;
+    }
+
+    public int getSubscriptionId() {
+        return subscriptionId;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        StreamingAppIdentifier that = (StreamingAppIdentifier) o;
+
+        if (uid != that.uid) {
+            return false;
+        }
+        if (subscriptionId != that.subscriptionId) {
+            return false;
+        }
+        return appName.equals(that.appName);
+    }
+
+    @Override
+    public int hashCode() {
+        int result = uid;
+        result = 31 * result + (appName != null ? appName.hashCode() : 0);
+        result = 31 * result + subscriptionId;
+        return result;
+    }
+}
diff --git a/testapps/EmbmsTestStreamingApp/res/layout/activity_main.xml b/testapps/EmbmsTestStreamingApp/res/layout/activity_main.xml
index 866e127..028c443 100644
--- a/testapps/EmbmsTestStreamingApp/res/layout/activity_main.xml
+++ b/testapps/EmbmsTestStreamingApp/res/layout/activity_main.xml
@@ -20,9 +20,30 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
-    <Button
-        android:id="@+id/bind_button"
-        android:layout_width="wrap_content"
+    <TextView
+        android:id="@+id/curr_streaming_uri"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:text="@string/bind_button" />
+        android:label="@string/streaming_uri_label"/>
+    <GridLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical" >
+        <Button
+            android:id="@+id/bind_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/bind_button" />
+        <Button
+            android:id="@+id/get_streaming_services_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/get_streaming_services_button" />
+    </GridLayout>
+    <Spinner
+        android:id="@+id/available_streaming_services"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:label="@string/available_streaming_services_label"/>
 </LinearLayout>
diff --git a/testapps/EmbmsTestStreamingApp/res/values/donottranslate_strings.xml b/testapps/EmbmsTestStreamingApp/res/values/donottranslate_strings.xml
index 71808fa..4895b2d 100644
--- a/testapps/EmbmsTestStreamingApp/res/values/donottranslate_strings.xml
+++ b/testapps/EmbmsTestStreamingApp/res/values/donottranslate_strings.xml
@@ -17,4 +17,7 @@
 
 <resources>
     <string name="bind_button">Bind to service</string>
+    <string name="streaming_uri_label">Current Streaming URI</string>
+    <string name="get_streaming_services_button">Get streaming services</string>
+    <string name="available_streaming_services_label">Available Streaming Services</string>
 </resources>
\ No newline at end of file
diff --git a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
index ed4fb6b..8369fde 100644
--- a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
+++ b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
@@ -17,21 +17,79 @@
 package com.android.phone.testapps.embmsfrontend;
 
 import android.app.Activity;
+import android.content.Context;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.telephony.MbmsStreamingManager;
 import android.telephony.mbms.MbmsException;
 import android.telephony.mbms.MbmsStreamingManagerCallback;
+import android.telephony.mbms.StreamingServiceInfo;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.AdapterView;
+import android.widget.ArrayAdapter;
 import android.widget.Button;
+import android.widget.Spinner;
+import android.widget.TextView;
 import android.widget.Toast;
 
+import java.util.List;
+
 public class EmbmsTestStreamingApp extends Activity {
-    private MbmsStreamingManagerCallback mStreamingListener = new MbmsStreamingManagerCallback() {};
-    private MbmsStreamingManager mStreamingManager;
+    private static final String APP_NAME = "StreamingApp1";
+
+    private MbmsStreamingManagerCallback mStreamingListener = new MbmsStreamingManagerCallback() {
+        @Override
+        public void streamingServicesUpdated(List<StreamingServiceInfo> services) {
+            EmbmsTestStreamingApp.this.runOnUiThread(() ->
+                    Toast.makeText(EmbmsTestStreamingApp.this,
+                            "Got services length " + services.size(),
+                            Toast.LENGTH_SHORT).show());
+            updateStreamingServicesList(services);
+        }
+    };
+
+    private final class StreamingServiceInfoAdapter
+            extends ArrayAdapter<StreamingServiceInfo> {
+        public StreamingServiceInfoAdapter(Context context, int resource) {
+            super(context, resource);
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            StreamingServiceInfo info = getItem(position);
+            TextView result = new TextView(EmbmsTestStreamingApp.this);
+            result.setText(info.getClassName());
+            return result;
+        }
+
+        @Override
+        public View getDropDownView(int position, View convertView, ViewGroup parent) {
+            StreamingServiceInfo info = getItem(position);
+            TextView result = new TextView(EmbmsTestStreamingApp.this);
+            String text = "classname="
+                    + info.getClassName()
+                    + ", "
+                    + "serviceId="
+                    + info.getServiceId();
+            result.setText(text);
+            return result;
+        }
+
+        public void update(List<StreamingServiceInfo> services) {
+            clear();
+            addAll(services);
+        }
+    }
+
+    private MbmsStreamingManager mStreamingManager = null;
 
     private Handler mHandler;
     private HandlerThread mHandlerThread;
+
+    private StreamingServiceInfoAdapter mStreamingServicesDisplayAdapter;
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -40,13 +98,15 @@
         mHandlerThread = new HandlerThread("EmbmsSampleFrontendWorker");
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+        mStreamingServicesDisplayAdapter =
+                new StreamingServiceInfoAdapter(this, android.R.layout.simple_spinner_item);
 
         Button bindButton = (Button) findViewById(R.id.bind_button);
         bindButton.setOnClickListener((view) ->
             mHandler.post(() -> {
                 try {
                     mStreamingManager = MbmsStreamingManager.create(
-                            EmbmsTestStreamingApp.this, mStreamingListener, getPackageName());
+                            EmbmsTestStreamingApp.this, mStreamingListener, APP_NAME);
                 } catch (MbmsException e) {
                     EmbmsTestStreamingApp.this.runOnUiThread(() ->
                             Toast.makeText(EmbmsTestStreamingApp.this,
@@ -58,6 +118,43 @@
                                 Toast.LENGTH_SHORT).show());
             })
         );
+
+        Button getStreamingServicesButton = (Button)
+                findViewById(R.id.get_streaming_services_button);
+        getStreamingServicesButton.setOnClickListener((view) -> {
+            if (mStreamingManager == null) {
+                Toast.makeText(EmbmsTestStreamingApp.this,
+                        "No streaming service bound", Toast.LENGTH_SHORT).show();
+                return;
+            }
+            try {
+                mStreamingManager.getStreamingServices(null);
+            } catch (MbmsException e) {
+                Toast.makeText(EmbmsTestStreamingApp.this,
+                        "Error getting streaming services" + e.getErrorCode(),
+                        Toast.LENGTH_SHORT).show();
+            }
+        });
+
+        Spinner serviceSelector = (Spinner) findViewById(R.id.available_streaming_services);
+        mStreamingServicesDisplayAdapter.setDropDownViewResource(
+                android.R.layout.simple_spinner_dropdown_item);
+        serviceSelector.setAdapter(mStreamingServicesDisplayAdapter);
+        serviceSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
+            @Override
+            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
+                StreamingServiceInfo info =
+                        (StreamingServiceInfo) serviceSelector.getItemAtPosition(position);
+                Toast.makeText(EmbmsTestStreamingApp.this,
+                        "Service selected: " + info.getClassName(), Toast.LENGTH_SHORT).show();
+                // TODO: use this value for start streaming
+            }
+
+            @Override
+            public void onNothingSelected(AdapterView<?> parent) {
+
+            }
+        });
     }
 
     @Override
@@ -65,4 +162,8 @@
         super.onDestroy();
         mHandlerThread.quit();
     }
+
+    private void updateStreamingServicesList(List<StreamingServiceInfo> services) {
+        runOnUiThread(() -> mStreamingServicesDisplayAdapter.update(services));
+    }
 }