blob: 75a2c384e8b111135e724dd1a252878d10031ce0 [file] [log] [blame]
/*
* 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);
}
}