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