Home | History | Annotate | Download | only in stream
      1 /*
      2  * Copyright (c) 2016, The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.car.stream;
     17 
     18 import android.app.Service;
     19 import android.content.Intent;
     20 import android.os.Binder;
     21 import android.os.DeadObjectException;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 import android.util.Log;
     25 import android.util.Pair;
     26 
     27 import java.util.ArrayList;
     28 import java.util.Iterator;
     29 import java.util.LinkedHashMap;
     30 import java.util.List;
     31 
     32 /**
     33  * A service that manages the {@link StreamCard} being generated by the system and notifies
     34  * the {@link IStreamConsumer} that new cards are available.
     35  */
     36 public class StreamService extends Service {
     37     private static final String TAG = "StreamService";
     38     private static final int DEFAULT_STREAM_CONSUMER_COUNT = 3;
     39 
     40     // The StreamCard is identified by a key which is comprised of its type and id
     41     private LinkedHashMap<Pair<Integer, Long>, StreamCard> mStreamCards = new LinkedHashMap<>();
     42 
     43     private List<IStreamConsumer> mConsumers = new ArrayList<>(DEFAULT_STREAM_CONSUMER_COUNT);
     44 
     45     private final IBinder mStreamProducerBinder = new StreamProducerBinder();
     46 
     47 
     48     public class StreamProducerBinder extends Binder {
     49         StreamService getService() {
     50             return StreamService.this;
     51         }
     52     }
     53 
     54     @Override
     55     public IBinder onBind(Intent intent) {
     56         if (Log.isLoggable(TAG, Log.DEBUG)) {
     57             Log.d(TAG, "onBind() calling process ID: " + Binder.getCallingPid()
     58                     + " StreamService process ID: " + android.os.Process.myPid());
     59         }
     60 
     61         String action = intent.getAction();
     62         switch(action){
     63             case StreamConstants.STREAM_PRODUCER_BIND_ACTION:
     64                 return mStreamProducerBinder;
     65             case StreamConstants.STREAM_CONSUMER_BIND_ACTION:
     66                 return mStreamConsumerService;
     67             default:
     68                 return null;
     69         }
     70     }
     71 
     72     private final IBinder mStreamConsumerService = new IStreamService.Stub() {
     73         @Override
     74         public void registerConsumer(IStreamConsumer consumer) throws RemoteException {
     75             mConsumers.add(consumer);
     76 
     77             if (Log.isLoggable(TAG, Log.DEBUG)) {
     78                 Log.d(TAG, "Consumer registered, total # consumers: " + mConsumers.size());
     79             }
     80         }
     81 
     82         @Override
     83         public void unregisterConsumer(IStreamConsumer consumer) throws RemoteException {
     84             mConsumers.remove(consumer);
     85 
     86             if (Log.isLoggable(TAG, Log.DEBUG)) {
     87                 Log.d(TAG, "Consumer removed, total # consumers: " + mConsumers.size());
     88             }
     89         }
     90 
     91         @Override
     92         public List<StreamCard> fetchAllStreamCards() throws RemoteException {
     93             if (Log.isLoggable(TAG, Log.DEBUG)) {
     94                 Log.d(TAG, "Fetching all stream items, # cards: " + mStreamCards.size());
     95             }
     96 
     97             List<StreamCard> cards = new ArrayList(mStreamCards.values());
     98             return cards;
     99         }
    100 
    101         @Override
    102         public void notifyStreamCardDismissed(StreamCard card) throws RemoteException {
    103             if (Log.isLoggable(TAG, Log.DEBUG)) {
    104                 Log.d(TAG, "StreamCard dismissed");
    105             }
    106         }
    107 
    108         @Override
    109         public void notifyStreamCardInteracted(StreamCard card) throws RemoteException {
    110             if (Log.isLoggable(TAG, Log.DEBUG)) {
    111                 Log.d(TAG, "StreamCard clicked");
    112             }
    113         }
    114     };
    115 
    116     /**
    117      * Add a {@link StreamCard} to the StreamService. The {@link StreamCard} will be published to
    118      * all IStreamListener registered with the StreamService.
    119      */
    120     public void addStreamCard(StreamCard card) {
    121         if (card == null) {
    122             return;
    123         }
    124         rankStreamCard(card);
    125         mStreamCards.put(getStreamCardKey(card), card);
    126         notifyListenersCardAdded(card);
    127     }
    128 
    129     /**
    130      * Remove a {@link StreamCard} to the StreamService. All registered {@link IStreamConsumer} will
    131      * be notified of the removal.
    132      *
    133      * @param card
    134      */
    135     public void removeStreamCard(StreamCard card) {
    136         if (Log.isLoggable(TAG, Log.DEBUG)) {
    137             Log.d(TAG, "Stream Card Removed: " + card.toString());
    138         }
    139 
    140         if (card == null) {
    141             return;
    142         }
    143 
    144         mStreamCards.remove(getStreamCardKey(card));
    145         notifyListenersCardRemoved(card);
    146     }
    147 
    148     private Pair<Integer, Long> getStreamCardKey(StreamCard card) {
    149         return new Pair(card.getType(), card.getId());
    150     }
    151 
    152     private void notifyListenersCardAdded(StreamCard card) {
    153         Iterator<IStreamConsumer> iterator = mConsumers.iterator();
    154 
    155         while (iterator.hasNext()) {
    156             IStreamConsumer consumer = iterator.next();
    157             try {
    158                 consumer.onStreamCardAdded(card);
    159             } catch (DeadObjectException e) {
    160                 iterator.remove();
    161                 Log.w(TAG, "Dead Stream Listener removed");
    162             } catch (RemoteException e) {
    163                 Log.e(TAG, e.getMessage());
    164             }
    165         }
    166 
    167         if (Log.isLoggable(TAG, Log.DEBUG)) {
    168             Log.d(TAG, "Notify StreamCard added, card: " + card);
    169             Log.d(TAG, "Card Extension: " + card.getCardExtension());
    170         }
    171     }
    172 
    173     private void notifyListenersCardRemoved(StreamCard card) {
    174         Iterator<IStreamConsumer> iterator = mConsumers.iterator();
    175 
    176         while (iterator.hasNext()) {
    177             IStreamConsumer consumer = iterator.next();
    178             try {
    179                 consumer.onStreamCardRemoved(card);
    180             } catch (DeadObjectException e) {
    181                 iterator.remove();
    182                 Log.w(TAG, "Dead Stream Listener removed");
    183             } catch (RemoteException e) {
    184                 Log.e(TAG, e.getMessage());
    185             }
    186         }
    187 
    188         if (Log.isLoggable(TAG, Log.DEBUG)) {
    189             Log.d(TAG, "Notify StreamCard removed, card type: " + card.getType());
    190         }
    191     }
    192 
    193     private void rankStreamCard(StreamCard card) {
    194         // TODO: move this into a separate class once we introduce the actual ranking.
    195         card.setPriority(1);
    196     }
    197 }
    198