blob: 758ce9e9b16575b5cf90e548f0b304b371deedb0 [file] [log] [blame]
/*
* 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 android.car.cluster;
import android.car.CarManagerBase;
import android.car.CarNotConnectedException;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* API to work with instrument cluster.
*
* @hide
*/
public class CarInstrumentClusterManager implements CarManagerBase {
private static final String TAG = CarInstrumentClusterManager.class.getSimpleName();
/** @hide */
public static final String CATEGORY_NAVIGATION = "android.car.cluster.NAVIGATION";
/**
* When activity in the cluster is launched it will receive {@link ClusterActivityState} in the
* intent's extra thus activity will know information about unobscured area, etc. upon activity
* creation.
*
* @hide
*/
public static final String KEY_EXTRA_ACTIVITY_STATE =
"android.car.cluster.ClusterActivityState";
private final EventHandler mHandler;
private final Map<String, Set<Callback>> mCallbacksByCategory = new HashMap<>(0);
private final Object mLock = new Object();
private final Map<String, Bundle> mActivityStatesByCategory = new HashMap<>(0);
private final IInstrumentClusterManagerService mService;
private ClusterManagerCallback mServiceToManagerCallback;
/**
* Starts activity in the instrument cluster.
*
* @hide
*/
public void startActivity(Intent intent) throws CarNotConnectedException {
try {
mService.startClusterActivity(intent);
} catch (RemoteException e) {
throw new CarNotConnectedException(e);
}
}
/**
* Caller of this method will receive immediate callback with the most recent state if state
* exists for given category.
*
* @param category category of the activity in the cluster,
* see {@link #CATEGORY_NAVIGATION}
* @param callback instance of {@link Callback} class to receive events.
*
* @hide
*/
public void registerCallback(String category, Callback callback)
throws CarNotConnectedException {
Log.i(TAG, "registerCallback, category: " + category + ", callback: " + callback);
ClusterManagerCallback callbackToCarService = null;
synchronized (mLock) {
Set<Callback> callbacks = mCallbacksByCategory.get(category);
if (callbacks == null) {
callbacks = new HashSet<>(1);
mCallbacksByCategory.put(category, callbacks);
}
if (!callbacks.add(callback)) {
Log.w(TAG, "registerCallback: already registered");
return; // already registered
}
if (mActivityStatesByCategory.containsKey(category)) {
Log.i(TAG, "registerCallback: sending activity state...");
callback.onClusterActivityStateChanged(
category, mActivityStatesByCategory.get(category));
}
if (mServiceToManagerCallback == null) {
Log.i(TAG, "registerCallback: registering callback with car service...");
mServiceToManagerCallback = new ClusterManagerCallback();
callbackToCarService = mServiceToManagerCallback;
}
}
try {
mService.registerCallback(callbackToCarService);
Log.i(TAG, "registerCallback: done");
} catch (RemoteException e) {
throw new CarNotConnectedException(e);
}
}
/**
* Unregisters given callback for all activity categories.
*
* @param callback previously registered callback
*
* @hide
*/
public void unregisterCallback(Callback callback) throws CarNotConnectedException {
List<String> keysToRemove = new ArrayList<>(1);
synchronized (mLock) {
for (Map.Entry<String, Set<Callback>> entry : mCallbacksByCategory.entrySet()) {
Set<Callback> callbacks = entry.getValue();
if (callbacks.remove(callback) && callbacks.isEmpty()) {
keysToRemove.add(entry.getKey());
}
}
for (String key: keysToRemove) {
mCallbacksByCategory.remove(key);
}
if (mCallbacksByCategory.isEmpty()) {
try {
mService.unregisterCallback(mServiceToManagerCallback);
} catch (RemoteException e) {
throw new CarNotConnectedException(e);
}
mServiceToManagerCallback = null;
}
}
}
/** @hide */
public CarInstrumentClusterManager(IBinder service, Handler handler) {
mService = IInstrumentClusterManagerService.Stub.asInterface(service);
mHandler = new EventHandler(handler.getLooper());
}
/** @hide */
public interface Callback {
/**
* Notify client that activity state was changed.
*
* @param category cluster activity category, see {@link #CATEGORY_NAVIGATION}
* @param clusterActivityState see {@link ClusterActivityState} how to read this bundle.
*/
void onClusterActivityStateChanged(String category, Bundle clusterActivityState);
}
/** @hide */
@Override
public void onCarDisconnected() {
}
private class EventHandler extends Handler {
final static int MSG_ACTIVITY_STATE = 1;
EventHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
Log.i(TAG, "handleMessage, message: " + msg);
switch (msg.what) {
case MSG_ACTIVITY_STATE:
Pair<String, Bundle> info = (Pair<String, Bundle>) msg.obj;
String category = info.first;
Bundle state = info.second;
List<CarInstrumentClusterManager.Callback> callbacks = null;
synchronized (mLock) {
if (mCallbacksByCategory.containsKey(category)) {
callbacks = new ArrayList<>(mCallbacksByCategory.get(category));
}
}
Log.i(TAG, "handleMessage, callbacks: " + callbacks);
if (callbacks != null) {
for (CarInstrumentClusterManager.Callback cb : callbacks) {
cb.onClusterActivityStateChanged(category, state);
}
}
break;
default:
Log.e(TAG, "Unexpected message: " + msg.what);
}
}
}
private class ClusterManagerCallback extends IInstrumentClusterManagerCallback.Stub {
@Override
public void setClusterActivityState(String category, Bundle clusterActivityState)
throws RemoteException {
Log.i(TAG, "setClusterActivityState, category: " + category);
synchronized (mLock) {
mActivityStatesByCategory.put(category, clusterActivityState);
}
mHandler.sendMessage(mHandler.obtainMessage(EventHandler.MSG_ACTIVITY_STATE,
new Pair<>(category, clusterActivityState)));
}
}
}