1/* 2 * Copyright (C) 2017 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 */ 16package android.car.storagemonitoring; 17 18import android.annotation.RequiresPermission; 19import android.annotation.SystemApi; 20import android.car.Car; 21import android.car.CarManagerBase; 22import android.car.CarNotConnectedException; 23import android.os.Handler; 24import android.os.IBinder; 25import android.os.RemoteException; 26import com.android.car.internal.SingleMessageHandler; 27import java.lang.ref.WeakReference; 28import java.util.Collections; 29import java.util.HashSet; 30import java.util.List; 31import java.util.Set; 32 33import static android.car.CarApiUtil.checkCarNotConnectedExceptionFromCarService; 34 35/** 36 * API for retrieving information and metrics about the flash storage. 37 * 38 * @hide 39 */ 40@SystemApi 41public final class CarStorageMonitoringManager implements CarManagerBase { 42 private static final String TAG = CarStorageMonitoringManager.class.getSimpleName(); 43 private static final int MSG_IO_STATS_EVENT = 0; 44 45 private final ICarStorageMonitoring mService; 46 private ListenerToService mListenerToService; 47 private final SingleMessageHandler<IoStats> mMessageHandler; 48 private final Set<IoStatsListener> mListeners = new HashSet<>(); 49 50 public interface IoStatsListener { 51 void onSnapshot(IoStats snapshot); 52 } 53 private static final class ListenerToService extends IIoStatsListener.Stub { 54 private final WeakReference<CarStorageMonitoringManager> mManager; 55 56 ListenerToService(CarStorageMonitoringManager manager) { 57 mManager = new WeakReference<>(manager); 58 } 59 60 @Override 61 public void onSnapshot(IoStats snapshot) { 62 CarStorageMonitoringManager manager = mManager.get(); 63 if (manager != null) { 64 manager.mMessageHandler.sendEvents(Collections.singletonList(snapshot)); 65 } 66 } 67 } 68 69 public static final String INTENT_EXCESSIVE_IO = "android.car.storagemonitoring.EXCESSIVE_IO"; 70 71 public static final int PRE_EOL_INFO_UNKNOWN = 0; 72 public static final int PRE_EOL_INFO_NORMAL = 1; 73 public static final int PRE_EOL_INFO_WARNING = 2; 74 public static final int PRE_EOL_INFO_URGENT = 3; 75 76 public static final long SHUTDOWN_COST_INFO_MISSING = -1; 77 78 /** 79 * @hide 80 */ 81 public CarStorageMonitoringManager(IBinder service, Handler handler) { 82 mService = ICarStorageMonitoring.Stub.asInterface(service); 83 mMessageHandler = new SingleMessageHandler<IoStats>(handler, MSG_IO_STATS_EVENT) { 84 @Override 85 protected void handleEvent(IoStats event) { 86 for (IoStatsListener listener : mListeners) { 87 listener.onSnapshot(event); 88 } 89 } 90 }; 91 } 92 93 /** 94 * @hide 95 */ 96 @Override 97 public void onCarDisconnected() { 98 mListeners.clear(); 99 mListenerToService = null; 100 } 101 102 // ICarStorageMonitoring forwards 103 104 /** 105 * This method returns the value of the "pre EOL" indicator for the flash storage 106 * as retrieved during the current boot cycle. 107 * 108 * It will return either PRE_EOL_INFO_UNKNOWN if the value can't be determined, 109 * or one of PRE_EOL_INFO_{NORMAL|WARNING|URGENT} depending on the device state. 110 */ 111 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 112 public int getPreEolIndicatorStatus() throws CarNotConnectedException { 113 try { 114 return mService.getPreEolIndicatorStatus(); 115 } catch (IllegalStateException e) { 116 checkCarNotConnectedExceptionFromCarService(e); 117 } catch (RemoteException e) { 118 throw new CarNotConnectedException(); 119 } 120 return PRE_EOL_INFO_UNKNOWN; 121 } 122 123 /** 124 * This method returns the value of the wear estimate indicators for the flash storage 125 * as retrieved during the current boot cycle. 126 * 127 * The indicators are guaranteed to be a lower-bound on the actual wear of the storage. 128 * Current technology in common automotive usage offers estimates in 10% increments. 129 * 130 * If either or both indicators are not available, they will be reported as UNKNOWN. 131 */ 132 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 133 public WearEstimate getWearEstimate() throws CarNotConnectedException { 134 try { 135 return mService.getWearEstimate(); 136 } catch (IllegalStateException e) { 137 checkCarNotConnectedExceptionFromCarService(e); 138 } catch (RemoteException e) { 139 throw new CarNotConnectedException(); 140 } 141 return WearEstimate.UNKNOWN_ESTIMATE; 142 } 143 144 /** 145 * This method returns a list of all changes in wear estimate indicators detected during the 146 * lifetime of the system. 147 * 148 * The indicators are not guaranteed to persist across a factory reset. 149 * 150 * The indicators are guaranteed to be a lower-bound on the actual wear of the storage. 151 * Current technology in common automotive usage offers estimates in 10% increments. 152 * 153 * If no indicators are available, an empty list will be returned. 154 */ 155 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 156 public List<WearEstimateChange> getWearEstimateHistory() throws CarNotConnectedException { 157 try { 158 return mService.getWearEstimateHistory(); 159 } catch (IllegalStateException e) { 160 checkCarNotConnectedExceptionFromCarService(e); 161 } catch (RemoteException e) { 162 throw new CarNotConnectedException(); 163 } 164 return Collections.emptyList(); 165 } 166 167 /** 168 * This method returns a list of per user-id I/O activity metrics as collected at the end of 169 * system boot. 170 * 171 * The BOOT_COMPLETE broadcast is used as the trigger to collect this data. The implementation 172 * may impose an additional, and even variable across boot cycles, delay between the sending 173 * of the broadcast and the collection of the data. 174 * 175 * If the information is not available, an empty list will be returned. 176 */ 177 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 178 public List<IoStatsEntry> getBootIoStats() throws CarNotConnectedException { 179 try { 180 return mService.getBootIoStats(); 181 } catch (IllegalStateException e) { 182 checkCarNotConnectedExceptionFromCarService(e); 183 } catch (RemoteException e) { 184 throw new CarNotConnectedException(); 185 } 186 return Collections.emptyList(); 187 } 188 189 /** 190 * This method returns an approximation of the number of bytes written to disk during 191 * the course of the previous system shutdown. 192 * 193 * <p>For purposes of this API the system shutdown is defined as starting when CarService 194 * receives the ACTION_SHUTDOWN or ACTION_REBOOT intent from the system.</p> 195 * 196 * <p>The information provided by this API does not provide attribution of the disk writes to 197 * specific applications or system daemons.</p> 198 * 199 * <p>The information returned by this call is a best effort guess, whose accuracy depends 200 * on the underlying file systems' ability to reliably track and accumulate 201 * disk write sizes.</p> 202 * 203 * <p>A corrupt file system, or one which was not cleanly unmounted during shutdown, may 204 * be unable to provide any information, or may provide incorrect data. While the API 205 * will attempt to detect these scenarios, the detection may fail and incorrect data 206 * may end up being used in calculations.</p> 207 * 208 * <p>If the information is not available, SHUTDOWN_COST_INFO_MISSING will be returned.</p>s 209 */ 210 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 211 public long getShutdownDiskWriteAmount() throws CarNotConnectedException { 212 try { 213 return mService.getShutdownDiskWriteAmount(); 214 } catch (IllegalStateException e) { 215 checkCarNotConnectedExceptionFromCarService(e); 216 } catch (RemoteException e) { 217 throw new CarNotConnectedException(); 218 } 219 return SHUTDOWN_COST_INFO_MISSING; 220 } 221 222 /** 223 * This method returns a list of per user-id I/O activity metrics as collected from kernel 224 * start until the last snapshot. 225 * 226 * The samples provided might be as old as the value of the ioStatsRefreshRateSeconds setting. 227 * 228 * If the information is not available, an empty list will be returned. 229 */ 230 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 231 public List<IoStatsEntry> getAggregateIoStats() throws CarNotConnectedException { 232 try { 233 return mService.getAggregateIoStats(); 234 } catch (IllegalStateException e) { 235 checkCarNotConnectedExceptionFromCarService(e); 236 } catch (RemoteException e) { 237 throw new CarNotConnectedException(); 238 } 239 return Collections.emptyList(); 240 } 241 242 /** 243 * This method returns a list of the I/O stats deltas currently stored by the system. 244 * 245 * Periodically, the system gathers I/O activity metrics and computes and stores a delta from 246 * the previous cycle. The timing and the number of these stored samples are configurable 247 * by the OEM. 248 * 249 * The samples are returned in order from the oldest to the newest. 250 * 251 * If the information is not available, an empty list will be returned. 252 */ 253 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 254 public List<IoStats> getIoStatsDeltas() throws CarNotConnectedException { 255 try { 256 return mService.getIoStatsDeltas(); 257 } catch (IllegalStateException e) { 258 checkCarNotConnectedExceptionFromCarService(e); 259 } catch (RemoteException e) { 260 throw new CarNotConnectedException(); 261 } 262 return Collections.emptyList(); 263 } 264 265 /** 266 * This method registers a new listener to receive I/O stats deltas. 267 * 268 * The system periodically gathers I/O activity metrics and computes a delta of such 269 * activity. Registered listeners will receive those deltas as they are available. 270 * 271 * The timing of availability of the deltas is configurable by the OEM. 272 */ 273 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 274 public void registerListener(IoStatsListener listener) throws CarNotConnectedException { 275 try { 276 if (mListeners.isEmpty()) { 277 if (mListenerToService == null) { 278 mListenerToService = new ListenerToService(this); 279 } 280 mService.registerListener(mListenerToService); 281 } 282 mListeners.add(listener); 283 } catch (IllegalStateException e) { 284 checkCarNotConnectedExceptionFromCarService(e); 285 } catch (RemoteException e) { 286 throw new CarNotConnectedException(); 287 } 288 } 289 290 /** 291 * This method removes a registered listener of I/O stats deltas. 292 */ 293 @RequiresPermission(value=Car.PERMISSION_STORAGE_MONITORING) 294 public void unregisterListener(IoStatsListener listener) throws CarNotConnectedException { 295 try { 296 if (!mListeners.remove(listener)) { 297 return; 298 } 299 if (mListeners.isEmpty()) { 300 mService.unregisterListener(mListenerToService); 301 mListenerToService = null; 302 } 303 } catch (IllegalStateException e) { 304 checkCarNotConnectedExceptionFromCarService(e); 305 } catch (RemoteException e) { 306 throw new CarNotConnectedException(); 307 } 308 } 309} 310