| /* |
| * Copyright (C) 2020 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.google.android.connecteddevice.notificationmsg; |
| |
| import static com.google.android.connecteddevice.util.SafeLog.logd; |
| import static com.google.android.connecteddevice.util.SafeLog.loge; |
| import static com.google.android.connecteddevice.util.SafeLog.logi; |
| import static com.google.android.connecteddevice.util.SafeLog.logw; |
| |
| import android.content.Context; |
| import android.os.ParcelUuid; |
| import androidx.annotation.NonNull; |
| import com.google.android.connecteddevice.api.RemoteFeature; |
| import com.google.android.connecteddevice.model.ConnectedDevice; |
| import com.google.android.connecteddevice.notificationmsg.proto.NotificationMsg; |
| import com.google.android.connecteddevice.notificationmsg.proto.NotificationMsg.PhoneToCarMessage; |
| import com.google.protobuf.ExtensionRegistryLite; |
| import com.google.protobuf.InvalidProtocolBufferException; |
| |
| /** |
| * An implementation of {@link RemoteFeature} that registers the NotificationMsg feature and handles |
| * the import and deletion of text messages sent from connected {@link ConnectedDevice}s. |
| * |
| * <p>The communication data this feature is handling will be cleared every time an error or |
| * disconnect happens, or the feature is stopped. |
| */ |
| public class NotificationMsgFeature extends RemoteFeature { |
| private static final String TAG = "NotificationMsgFeature"; |
| |
| private static final ParcelUuid FEATURE_ID = |
| ParcelUuid.fromString("b2337f58-18ff-4f92-a0cf-4df63ab2c889"); |
| |
| private final NotificationMsgDelegate notificationMsgDelegate; |
| private ConnectedDevice secureDeviceForActiveUser; |
| |
| NotificationMsgFeature( |
| @NonNull Context context, @NonNull NotificationMsgDelegate notificationMsgDelegate) { |
| super(context, FEATURE_ID); |
| this.notificationMsgDelegate = notificationMsgDelegate; |
| } |
| |
| @Override |
| public void start() { |
| // For safety in case something went wrong and the feature couldn't terminate correctly we |
| // clear all notifications and local data on start of the feature. |
| notificationMsgDelegate.cleanupMessagesAndNotifications(key -> true); |
| super.start(); |
| logi(TAG, "Feature started"); |
| } |
| |
| @Override |
| public void stop() { |
| // Erase all the notifications and local data, so that no user data stays on the device |
| // after the feature is stopped. |
| notificationMsgDelegate.onDestroy(); |
| super.stop(); |
| logi(TAG, "Feature stopped"); |
| } |
| |
| @Override |
| public void onReady() { |
| logi(TAG, "Feature ready"); |
| } |
| |
| @Override |
| public void onNotReady() { |
| logi(TAG, "Feature not ready"); |
| } |
| |
| @Override |
| protected void onMessageReceived(ConnectedDevice device, byte[] message) { |
| if (secureDeviceForActiveUser == null |
| && device.hasSecureChannel()) { |
| logw( |
| TAG, |
| "stored secure device is null, but message was received on a" |
| + " secure device!" |
| + device); |
| secureDeviceForActiveUser = device; |
| } |
| if (!isSecureDeviceForActiveUser(device.getDeviceId())) { |
| logd(TAG, device + ": skipped message from unsecure device"); |
| return; |
| } |
| logd(TAG, device + ": received message over secure channel"); |
| try { |
| PhoneToCarMessage phoneToCarMessage = |
| NotificationMsg.PhoneToCarMessage.parseFrom( |
| message, ExtensionRegistryLite.getEmptyRegistry()); |
| notificationMsgDelegate.onMessageReceived(device, phoneToCarMessage); |
| } catch (InvalidProtocolBufferException e) { |
| loge(TAG, device + ": error parsing notification msg protobuf", e); |
| } |
| } |
| |
| @Override |
| protected void onSecureChannelEstablished(ConnectedDevice device) { |
| logd(TAG, "received secure device: " + device); |
| secureDeviceForActiveUser = device; |
| } |
| |
| @Override |
| protected void onDeviceError(ConnectedDevice device, int error) { |
| if (!isSecureDeviceForActiveUser(device.getDeviceId())) { |
| return; |
| } |
| loge(TAG, device + ": received device error " + error, null); |
| } |
| |
| @Override |
| protected void onDeviceDisconnected(ConnectedDevice device) { |
| if (!isSecureDeviceForActiveUser(device.getDeviceId())) { |
| return; |
| } |
| logw(TAG, device + ": disconnected"); |
| notificationMsgDelegate.onDeviceDisconnected(device.getDeviceId()); |
| secureDeviceForActiveUser = null; |
| } |
| |
| protected void sendData(@NonNull String deviceId, @NonNull byte[] message) { |
| if (secureDeviceForActiveUser == null || !isSecureDeviceForActiveUser(deviceId)) { |
| logw(TAG, "Could not send message to device: " + deviceId); |
| return; |
| } |
| |
| sendMessageSecurely(deviceId, message); |
| } |
| |
| @Override |
| protected void onMessageFailedToSend(String deviceId, byte[] message, boolean isTransient) { |
| // TODO (b/144924164): Notify Delegate action request failed. |
| } |
| |
| private boolean isSecureDeviceForActiveUser(String deviceId) { |
| return (secureDeviceForActiveUser != null) |
| && secureDeviceForActiveUser.getDeviceId().equals(deviceId); |
| } |
| } |