1 /*
2  * Copyright (C) 2015 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 
17 package com.android.commands.sm;
18 
19 import android.os.IVoldTaskListener;
20 import android.os.PersistableBundle;
21 import android.os.RemoteException;
22 import android.os.ServiceManager;
23 import android.os.SystemProperties;
24 import android.os.storage.DiskInfo;
25 import android.os.storage.IStorageManager;
26 import android.os.storage.StorageManager;
27 import android.os.storage.VolumeInfo;
28 import android.util.Log;
29 
30 import java.util.concurrent.CompletableFuture;
31 
32 public final class Sm {
33     private static final String TAG = "Sm";
34     private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY =
35             "persist.sys.vold_app_data_isolation_enabled";
36 
37     IStorageManager mSm;
38 
39     private String[] mArgs;
40     private int mNextArg;
41     private String mCurArgData;
42 
main(String[] args)43     public static void main(String[] args) {
44         boolean success = false;
45         try {
46             new Sm().run(args);
47             success = true;
48         } catch (Exception e) {
49             if (e instanceof IllegalArgumentException) {
50                 showUsage();
51                 System.exit(1);
52             }
53             Log.e(TAG, "Error", e);
54             System.err.println("Error: " + e);
55         }
56         System.exit(success ? 0 : 1);
57     }
58 
run(String[] args)59     public void run(String[] args) throws Exception {
60         if (args.length < 1) {
61             throw new IllegalArgumentException();
62         }
63 
64         mSm = IStorageManager.Stub.asInterface(ServiceManager.getService("mount"));
65         if (mSm == null) {
66             throw new RemoteException("Failed to find running mount service");
67         }
68 
69         mArgs = args;
70         String op = args[0];
71         mNextArg = 1;
72 
73         if ("list-disks".equals(op)) {
74             runListDisks();
75         } else if ("list-volumes".equals(op)) {
76             runListVolumes();
77         } else if ("has-adoptable".equals(op)) {
78             runHasAdoptable();
79         } else if ("get-primary-storage-uuid".equals(op)) {
80             runGetPrimaryStorageUuid();
81         } else if ("set-force-adoptable".equals(op)) {
82             runSetForceAdoptable();
83         } else if ("set-sdcardfs".equals(op)) {
84             runSetSdcardfs();
85         } else if ("partition".equals(op)) {
86             runPartition();
87         } else if ("mount".equals(op)) {
88             runMount();
89         } else if ("unmount".equals(op)) {
90             runUnmount();
91         } else if ("format".equals(op)) {
92             runFormat();
93         } else if ("benchmark".equals(op)) {
94             runBenchmark();
95         } else if ("forget".equals(op)) {
96             runForget();
97         } else if ("get-fbe-mode".equals(op)) {
98             runGetFbeMode();
99         } else if ("idle-maint".equals(op)) {
100             runIdleMaint();
101         } else if ("fstrim".equals(op)) {
102             runFstrim();
103         } else if ("set-virtual-disk".equals(op)) {
104             runSetVirtualDisk();
105         } else if ("start-checkpoint".equals(op)) {
106             runStartCheckpoint();
107         } else if ("supports-checkpoint".equals(op)) {
108             runSupportsCheckpoint();
109         } else if ("unmount-app-data-dirs".equals(op)) {
110             runDisableAppDataIsolation();
111         } else {
112             throw new IllegalArgumentException();
113         }
114     }
115 
runListDisks()116     public void runListDisks() throws RemoteException {
117         final boolean onlyAdoptable = "adoptable".equals(nextArg());
118         final DiskInfo[] disks = mSm.getDisks();
119         for (DiskInfo disk : disks) {
120             if (!onlyAdoptable || disk.isAdoptable()) {
121                 System.out.println(disk.getId());
122             }
123         }
124     }
125 
runListVolumes()126     public void runListVolumes() throws RemoteException {
127         final String filter = nextArg();
128         final int filterType;
129         if ("public".equals(filter)) {
130             filterType = VolumeInfo.TYPE_PUBLIC;
131         } else if ("private".equals(filter)) {
132             filterType = VolumeInfo.TYPE_PRIVATE;
133         } else if ("emulated".equals(filter)) {
134             filterType = VolumeInfo.TYPE_EMULATED;
135         } else if ("stub".equals(filter)) {
136             filterType = VolumeInfo.TYPE_STUB;
137         } else {
138             filterType = -1;
139         }
140 
141         final VolumeInfo[] vols = mSm.getVolumes(0);
142         for (VolumeInfo vol : vols) {
143             if (filterType == -1 || filterType == vol.getType()) {
144                 final String envState = VolumeInfo.getEnvironmentForState(vol.getState());
145                 System.out.println(vol.getId() + " " + envState + " " + vol.getFsUuid());
146             }
147         }
148     }
149 
runHasAdoptable()150     public void runHasAdoptable() {
151         System.out.println(StorageManager.hasAdoptable());
152     }
153 
runGetPrimaryStorageUuid()154     public void runGetPrimaryStorageUuid() throws RemoteException {
155         System.out.println(mSm.getPrimaryStorageUuid());
156     }
157 
runSetForceAdoptable()158     public void runSetForceAdoptable() throws RemoteException {
159         final int mask = StorageManager.DEBUG_ADOPTABLE_FORCE_ON
160                 | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF;
161         switch (nextArg()) {
162             case "on":
163             case "true":
164                 mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_ON, mask);
165                 break;
166             case "off":
167                 mSm.setDebugFlags(StorageManager.DEBUG_ADOPTABLE_FORCE_OFF, mask);
168                 break;
169             case "default":
170             case "false":
171                 mSm.setDebugFlags(0, mask);
172                 break;
173         }
174     }
175 
runSetSdcardfs()176     public void runSetSdcardfs() throws RemoteException {
177         final int mask = StorageManager.DEBUG_SDCARDFS_FORCE_ON
178                 | StorageManager.DEBUG_SDCARDFS_FORCE_OFF;
179         switch (nextArg()) {
180             case "on":
181                 mSm.setDebugFlags(StorageManager.DEBUG_SDCARDFS_FORCE_ON, mask);
182                 break;
183             case "off":
184                 mSm.setDebugFlags(StorageManager.DEBUG_SDCARDFS_FORCE_OFF, mask);
185                 break;
186             case "default":
187                 mSm.setDebugFlags(0, mask);
188                 break;
189         }
190     }
191 
runGetFbeMode()192     public void runGetFbeMode() {
193         if (StorageManager.isFileEncrypted()) {
194             System.out.println("native");
195         } else {
196             System.out.println("none");
197         }
198     }
199 
runPartition()200     public void runPartition() throws RemoteException {
201         final String diskId = nextArg();
202         final String type = nextArg();
203         if ("public".equals(type)) {
204             mSm.partitionPublic(diskId);
205         } else if ("private".equals(type)) {
206             mSm.partitionPrivate(diskId);
207         } else if ("mixed".equals(type)) {
208             final int ratio = Integer.parseInt(nextArg());
209             mSm.partitionMixed(diskId, ratio);
210         } else {
211             throw new IllegalArgumentException("Unsupported partition type " + type);
212         }
213     }
214 
runMount()215     public void runMount() throws RemoteException {
216         final String volId = nextArg();
217         mSm.mount(volId);
218     }
219 
runUnmount()220     public void runUnmount() throws RemoteException {
221         final String volId = nextArg();
222         mSm.unmount(volId);
223     }
224 
runFormat()225     public void runFormat() throws RemoteException {
226         final String volId = nextArg();
227         mSm.format(volId);
228     }
229 
runBenchmark()230     public void runBenchmark() throws Exception {
231         final String volId = nextArg();
232         final CompletableFuture<PersistableBundle> result = new CompletableFuture<>();
233         mSm.benchmark(volId, new IVoldTaskListener.Stub() {
234             @Override
235             public void onStatus(int status, PersistableBundle extras) {
236                 // Ignored
237             }
238 
239             @Override
240             public void onFinished(int status, PersistableBundle extras) {
241                 // Touch to unparcel
242                 extras.size();
243                 result.complete(extras);
244             }
245         });
246         System.out.println(result.get());
247     }
248 
runDisableAppDataIsolation()249     public void runDisableAppDataIsolation() throws RemoteException {
250         if (!SystemProperties.getBoolean(
251                 ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false)) {
252             System.err.println("Storage app data isolation is not enabled.");
253             return;
254         }
255         final String pkgName = nextArg();
256         final int pid = Integer.parseInt(nextArg());
257         final int userId = Integer.parseInt(nextArg());
258         mSm.disableAppDataIsolation(pkgName, pid, userId);
259     }
260 
runForget()261     public void runForget() throws RemoteException {
262         final String fsUuid = nextArg();
263         if ("all".equals(fsUuid)) {
264             mSm.forgetAllVolumes();
265         } else {
266             mSm.forgetVolume(fsUuid);
267         }
268     }
269 
runFstrim()270     public void runFstrim() throws Exception {
271         final CompletableFuture<PersistableBundle> result = new CompletableFuture<>();
272         mSm.fstrim(0, new IVoldTaskListener.Stub() {
273             @Override
274             public void onStatus(int status, PersistableBundle extras) {
275                 // Ignored
276             }
277 
278             @Override
279             public void onFinished(int status, PersistableBundle extras) {
280                 // Touch to unparcel
281                 extras.size();
282                 result.complete(extras);
283             }
284         });
285         System.out.println(result.get());
286     }
287 
runSetVirtualDisk()288     public void runSetVirtualDisk() throws RemoteException {
289         final boolean virtualDisk = Boolean.parseBoolean(nextArg());
290         mSm.setDebugFlags(virtualDisk ? StorageManager.DEBUG_VIRTUAL_DISK : 0,
291                 StorageManager.DEBUG_VIRTUAL_DISK);
292     }
293 
runIdleMaint()294     public void runIdleMaint() throws RemoteException {
295         final boolean im_run = "run".equals(nextArg());
296         if (im_run) {
297             mSm.runIdleMaintenance();
298         } else {
299             mSm.abortIdleMaintenance();
300         }
301     }
302 
runStartCheckpoint()303     private void runStartCheckpoint() throws RemoteException {
304         final String numRetriesString = nextArg();
305         if (numRetriesString == null) {
306             throw new IllegalArgumentException("Expected <num-retries>");
307         }
308         int numRetries;
309         try {
310             numRetries = Integer.parseInt(numRetriesString);
311         } catch (NumberFormatException e) {
312             throw new IllegalArgumentException("<num-retries> must be a positive integer");
313         }
314         if (numRetries <= 0) {
315             throw new IllegalArgumentException("<num-retries> must be a positive integer");
316         }
317         mSm.startCheckpoint(numRetries);
318     }
319 
runSupportsCheckpoint()320     private void runSupportsCheckpoint() throws RemoteException {
321         System.out.println(mSm.supportsCheckpoint());
322     }
323 
nextArg()324     private String nextArg() {
325         if (mNextArg >= mArgs.length) {
326             return null;
327         }
328         String arg = mArgs[mNextArg];
329         mNextArg++;
330         return arg;
331     }
332 
showUsage()333     private static int showUsage() {
334         System.err.println("usage: sm list-disks [adoptable]");
335         System.err.println("       sm list-volumes [public|private|emulated|stub|all]");
336         System.err.println("       sm has-adoptable");
337         System.err.println("       sm get-primary-storage-uuid");
338         System.err.println("       sm set-force-adoptable [on|off|default]");
339         System.err.println("       sm set-virtual-disk [true|false]");
340         System.err.println("");
341         System.err.println("       sm partition DISK [public|private|mixed] [ratio]");
342         System.err.println("       sm mount VOLUME");
343         System.err.println("       sm unmount VOLUME");
344         System.err.println("       sm format VOLUME");
345         System.err.println("       sm benchmark VOLUME");
346         System.err.println("       sm idle-maint [run|abort]");
347         System.err.println("       sm fstrim");
348         System.err.println("");
349         System.err.println("       sm forget [UUID|all]");
350         System.err.println("");
351         System.err.println("       sm start-checkpoint <num-retries>");
352         System.err.println("");
353         System.err.println("       sm supports-checkpoint");
354         System.err.println("");
355         System.err.println("       sm unmount-app-data-dirs PACKAGE_NAME PID USER_ID");
356         System.err.println("");
357         return 1;
358     }
359 }
360