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 { getService()49 StreamService getService() { 50 return StreamService.this; 51 } 52 } 53 54 @Override onBind(Intent intent)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 */ addStreamCard(StreamCard card)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 */ removeStreamCard(StreamCard card)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 getStreamCardKey(StreamCard card)148 private Pair<Integer, Long> getStreamCardKey(StreamCard card) { 149 return new Pair(card.getType(), card.getId()); 150 } 151 notifyListenersCardAdded(StreamCard card)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 notifyListenersCardRemoved(StreamCard card)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 rankStreamCard(StreamCard card)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