Android Auto Companion | 53b711e | 2020-12-11 22:13:57 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2020 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 | |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 17 | package com.google.android.connecteddevice.service; |
| 18 | |
| 19 | import static com.google.android.connecteddevice.util.SafeLog.logd; |
| 20 | import static com.google.android.connecteddevice.util.SafeLog.loge; |
| 21 | import static com.google.android.connecteddevice.util.SafeLog.logw; |
| 22 | |
| 23 | import android.content.ComponentName; |
| 24 | import android.content.Context; |
| 25 | import android.content.Intent; |
| 26 | import android.content.ServiceConnection; |
| 27 | import android.os.Handler; |
| 28 | import android.os.IBinder; |
| 29 | import android.os.Looper; |
| 30 | import androidx.annotation.NonNull; |
Android Auto Companion | 5908d6d | 2021-08-18 14:04:49 -0700 | [diff] [blame] | 31 | import androidx.annotation.VisibleForTesting; |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 32 | import com.google.common.collect.HashMultiset; |
| 33 | import com.google.common.collect.Multiset; |
| 34 | import java.time.Duration; |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 35 | import java.util.Map; |
| 36 | import java.util.concurrent.ConcurrentHashMap; |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 37 | |
| 38 | /** A service that is used to maintain the lifecycle of branch services. */ |
| 39 | public abstract class TrunkService extends MetaDataService { |
| 40 | |
| 41 | private static final String TAG = "TrunkService"; |
| 42 | |
| 43 | /** {@code string-array} List of services to start early. */ |
Android Auto Companion | 5908d6d | 2021-08-18 14:04:49 -0700 | [diff] [blame] | 44 | @VisibleForTesting |
| 45 | static final String META_EARLY_SERVICES = |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 46 | "com.google.android.connecteddevice.early_services"; |
| 47 | |
Android Auto Companion | 5908d6d | 2021-08-18 14:04:49 -0700 | [diff] [blame] | 48 | @VisibleForTesting |
| 49 | static final int MAX_BIND_ATTEMPTS = 3; |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 50 | |
Android Auto Companion | 5908d6d | 2021-08-18 14:04:49 -0700 | [diff] [blame] | 51 | private static final Duration BIND_RETRY_DURATION = Duration.ofSeconds(1); |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 52 | |
| 53 | private final Multiset<String> bindAttempts = HashMultiset.create(); |
| 54 | |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 55 | @SuppressWarnings("AndroidConcurrentHashMap") |
| 56 | private final Map<ComponentName, ServiceConnection> startedServices = new ConcurrentHashMap<>(); |
| 57 | |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 58 | @Override |
| 59 | public void onCreate() { |
| 60 | super.onCreate(); |
| 61 | startBranchServices(META_EARLY_SERVICES); |
| 62 | } |
| 63 | |
Android Auto Companion | 5908d6d | 2021-08-18 14:04:49 -0700 | [diff] [blame] | 64 | @Override |
| 65 | public void onDestroy() { |
| 66 | stopBranchServices(); |
| 67 | super.onDestroy(); |
| 68 | } |
| 69 | |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 70 | /** |
| 71 | * Start services defined in meta-data tag. |
| 72 | * |
| 73 | * @param metaDataName {@code android:name} in meta-data tag. |
| 74 | */ |
| 75 | protected final void startBranchServices(@NonNull String metaDataName) { |
| 76 | String[] services = getMetaStringArray(metaDataName, /* defaultValue= */ new String[0]); |
| 77 | if (services.length == 0) { |
| 78 | logw(TAG, "No services were found in " + metaDataName + "."); |
| 79 | return; |
| 80 | } |
| 81 | for (String service : services) { |
| 82 | ComponentName name = ComponentName.unflattenFromString(service); |
| 83 | if (name == null) { |
| 84 | loge(TAG, "Invalid branch service: " + service + ". Unable to start."); |
| 85 | continue; |
| 86 | } |
| 87 | bindToService(name); |
| 88 | } |
| 89 | } |
| 90 | |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 91 | /** Stop all branch services that have been started by this service. */ |
| 92 | protected final void stopBranchServices() { |
| 93 | logd(TAG, "Stopping connected branch services."); |
| 94 | for (Map.Entry<ComponentName, ServiceConnection> connection : startedServices.entrySet()) { |
| 95 | ComponentName name = connection.getKey(); |
| 96 | ServiceConnection serviceConnection = connection.getValue(); |
| 97 | logd(TAG, "Attempting to stop " + name.flattenToString()); |
| 98 | unbindService(serviceConnection); |
| 99 | } |
Android Auto Companion | ddfbeb6 | 2021-03-17 16:54:14 -0700 | [diff] [blame] | 100 | startedServices.clear(); |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 101 | } |
| 102 | |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 103 | private void bindToService(@NonNull ComponentName componentName) { |
| 104 | Intent intent = new Intent(); |
| 105 | intent.setComponent(componentName); |
| 106 | String flatComponentName = componentName.flattenToString(); |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 107 | boolean success = bindService(intent, createServiceConnection(), Context.BIND_AUTO_CREATE); |
| 108 | logd(TAG, "Attempted to start " + flatComponentName + " with success: " + success + "."); |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 109 | if (success) { |
| 110 | bindAttempts.remove(flatComponentName); |
| 111 | return; |
| 112 | } |
| 113 | bindAttempts.add(flatComponentName); |
| 114 | int attempts = bindAttempts.count(flatComponentName); |
| 115 | if (attempts > MAX_BIND_ATTEMPTS) { |
| 116 | loge( |
| 117 | TAG, |
Android Auto Companion | 16c608d | 2022-02-02 10:13:13 -0800 | [diff] [blame^] | 118 | "Failed to bind to " + flatComponentName + " after " + attempts + " attempts. Aborting."); |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 119 | return; |
| 120 | } |
| 121 | logw(TAG, "Unable to bind to " + flatComponentName + ". Trying again."); |
| 122 | new Handler(Looper.getMainLooper()) |
| 123 | .postDelayed(() -> bindToService(componentName), BIND_RETRY_DURATION.toMillis()); |
| 124 | } |
| 125 | |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 126 | private void onBranchServiceStarted(ComponentName name, ServiceConnection connection) { |
| 127 | logd(TAG, name.flattenToString() + " started successfully."); |
| 128 | startedServices.put(name, connection); |
| 129 | } |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 130 | |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 131 | private ServiceConnection createServiceConnection() { |
| 132 | return new ServiceConnection() { |
| 133 | @Override |
| 134 | public void onServiceConnected(ComponentName name, IBinder service) { |
| 135 | onBranchServiceStarted(name, this); |
| 136 | } |
| 137 | |
| 138 | @Override |
| 139 | public void onServiceDisconnected(ComponentName name) { |
| 140 | loge(TAG, "Lost connection to " + name.flattenToString() + ". Attempting to reconnect."); |
Android Auto Companion | ddfbeb6 | 2021-03-17 16:54:14 -0700 | [diff] [blame] | 141 | startedServices.remove(name); |
Android Auto Companion | c8fcd2b | 2021-02-02 21:22:08 +0000 | [diff] [blame] | 142 | bindAttempts.setCount(name.flattenToString(), 0); |
| 143 | bindToService(name); |
| 144 | } |
| 145 | |
| 146 | @Override |
| 147 | public void onNullBinding(ComponentName name) { |
| 148 | onBranchServiceStarted(name, this); |
| 149 | } |
| 150 | }; |
| 151 | } |
Erin Yan | cab226f | 2020-11-10 13:45:20 -0800 | [diff] [blame] | 152 | } |