| /* |
| * Copyright (c) 2016, 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.car.stream; |
| |
| import android.app.Service; |
| import android.content.Intent; |
| import android.os.Binder; |
| import android.os.DeadObjectException; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Log; |
| import android.util.Pair; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| |
| /** |
| * A service that manages the {@link StreamCard} being generated by the system and notifies |
| * the {@link IStreamConsumer} that new cards are available. |
| */ |
| public class StreamService extends Service { |
| private static final String TAG = "StreamService"; |
| private static final int DEFAULT_STREAM_CONSUMER_COUNT = 3; |
| |
| // The StreamCard is identified by a key which is comprised of its type and id |
| private LinkedHashMap<Pair<Integer, Long>, StreamCard> mStreamCards = new LinkedHashMap<>(); |
| |
| private List<IStreamConsumer> mConsumers = new ArrayList<>(DEFAULT_STREAM_CONSUMER_COUNT); |
| |
| private final IBinder mStreamProducerBinder = new StreamProducerBinder(); |
| |
| |
| public class StreamProducerBinder extends Binder { |
| StreamService getService() { |
| return StreamService.this; |
| } |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "onBind() calling process ID: " + Binder.getCallingPid() |
| + " StreamService process ID: " + android.os.Process.myPid()); |
| } |
| |
| String action = intent.getAction(); |
| switch(action){ |
| case StreamConstants.STREAM_PRODUCER_BIND_ACTION: |
| return mStreamProducerBinder; |
| case StreamConstants.STREAM_CONSUMER_BIND_ACTION: |
| return mStreamConsumerService; |
| default: |
| return null; |
| } |
| } |
| |
| private final IBinder mStreamConsumerService = new IStreamService.Stub() { |
| @Override |
| public void registerConsumer(IStreamConsumer consumer) throws RemoteException { |
| mConsumers.add(consumer); |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Consumer registered, total # consumers: " + mConsumers.size()); |
| } |
| } |
| |
| @Override |
| public void unregisterConsumer(IStreamConsumer consumer) throws RemoteException { |
| mConsumers.remove(consumer); |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Consumer removed, total # consumers: " + mConsumers.size()); |
| } |
| } |
| |
| @Override |
| public List<StreamCard> fetchAllStreamCards() throws RemoteException { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Fetching all stream items, # cards: " + mStreamCards.size()); |
| } |
| |
| List<StreamCard> cards = new ArrayList(mStreamCards.values()); |
| return cards; |
| } |
| |
| @Override |
| public void notifyStreamCardDismissed(StreamCard card) throws RemoteException { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "StreamCard dismissed"); |
| } |
| } |
| |
| @Override |
| public void notifyStreamCardInteracted(StreamCard card) throws RemoteException { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "StreamCard clicked"); |
| } |
| } |
| }; |
| |
| /** |
| * Add a {@link StreamCard} to the StreamService. The {@link StreamCard} will be published to |
| * all IStreamListener registered with the StreamService. |
| */ |
| public void addStreamCard(StreamCard card) { |
| if (card == null) { |
| return; |
| } |
| rankStreamCard(card); |
| mStreamCards.put(getStreamCardKey(card), card); |
| notifyListenersCardAdded(card); |
| } |
| |
| /** |
| * Remove a {@link StreamCard} to the StreamService. All registered {@link IStreamConsumer} will |
| * be notified of the removal. |
| * |
| * @param card |
| */ |
| public void removeStreamCard(StreamCard card) { |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Stream Card Removed: " + card.toString()); |
| } |
| |
| if (card == null) { |
| return; |
| } |
| |
| mStreamCards.remove(getStreamCardKey(card)); |
| notifyListenersCardRemoved(card); |
| } |
| |
| private Pair<Integer, Long> getStreamCardKey(StreamCard card) { |
| return new Pair(card.getType(), card.getId()); |
| } |
| |
| private void notifyListenersCardAdded(StreamCard card) { |
| Iterator<IStreamConsumer> iterator = mConsumers.iterator(); |
| |
| while (iterator.hasNext()) { |
| IStreamConsumer consumer = iterator.next(); |
| try { |
| consumer.onStreamCardAdded(card); |
| } catch (DeadObjectException e) { |
| iterator.remove(); |
| Log.w(TAG, "Dead Stream Listener removed"); |
| } catch (RemoteException e) { |
| Log.e(TAG, e.getMessage()); |
| } |
| } |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Notify StreamCard added, card: " + card); |
| Log.d(TAG, "Card Extension: " + card.getCardExtension()); |
| } |
| } |
| |
| private void notifyListenersCardRemoved(StreamCard card) { |
| Iterator<IStreamConsumer> iterator = mConsumers.iterator(); |
| |
| while (iterator.hasNext()) { |
| IStreamConsumer consumer = iterator.next(); |
| try { |
| consumer.onStreamCardRemoved(card); |
| } catch (DeadObjectException e) { |
| iterator.remove(); |
| Log.w(TAG, "Dead Stream Listener removed"); |
| } catch (RemoteException e) { |
| Log.e(TAG, e.getMessage()); |
| } |
| } |
| |
| if (Log.isLoggable(TAG, Log.DEBUG)) { |
| Log.d(TAG, "Notify StreamCard removed, card type: " + card.getType()); |
| } |
| } |
| |
| private void rankStreamCard(StreamCard card) { |
| // TODO: move this into a separate class once we introduce the actual ranking. |
| card.setPriority(1); |
| } |
| } |