summaryrefslogtreecommitdiff
path: root/sdksandbox/tests/testutils/src/android/app/sdksandbox/hosttestutils/AdoptableStorageUtils.java
blob: 7e62875f01931e4a45b8235daf99637a321f28b7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
 * Copyright (C) 2022 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 android.app.sdksandbox.hosttestutils;

import com.android.tradefed.log.LogUtil;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;

import java.util.Arrays;

public class AdoptableStorageUtils {

    private final BaseHostJUnit4Test mTest;

    private String mDiskId;

    public AdoptableStorageUtils(BaseHostJUnit4Test test) {
        mTest = test;
    }

    public boolean isAdoptableStorageSupported() throws Exception {
        boolean hasFeature =
                mTest.getDevice().hasFeature("feature:android.software.adoptable_storage");
        boolean hasFstab =
                Boolean.parseBoolean(
                        mTest.getDevice().executeShellCommand("sm has-adoptable").trim());
        return hasFeature && hasFstab;
    }

    // Creates a new volume in adoptable storage and returns its uuid
    public String createNewVolume() throws Exception {
        mDiskId = getAdoptionDisk();
        assertEmpty(mTest.getDevice().executeShellCommand("sm partition " + mDiskId + " private"));
        final LocalVolumeInfo vol = getAdoptionVolume();
        return vol.uuid;
    }

    // Destroy the volume created before
    public void cleanUpVolume() throws Exception {
        mTest.getDevice().executeShellCommand("sm partition " + mDiskId + " public");
        mTest.getDevice().executeShellCommand("sm forget all");
    }

    private String getAdoptionDisk() throws Exception {
        // In the case where we run multiple test we cleanup the state of the device. This
        // results in the execution of sm forget all which causes the MountService to "reset"
        // all its knowledge about available drives. This can cause the adoptable drive to
        // become temporarily unavailable.
        int attempt = 0;
        String disks = mTest.getDevice().executeShellCommand("sm list-disks adoptable");
        while ((disks == null || disks.isEmpty()) && attempt++ < 15) {
            Thread.sleep(1000);
            disks = mTest.getDevice().executeShellCommand("sm list-disks adoptable");
        }

        if (disks == null || disks.isEmpty()) {
            throw new AssertionError(
                    "Devices that claim to support adoptable storage must have "
                            + "adoptable media inserted during CTS to verify correct behavior");
        }
        return disks.split("\n")[0].trim();
    }

    private static void assertEmpty(String str) {
        if (str != null && str.trim().length() > 0) {
            throw new AssertionError("Expected empty string but found " + str);
        }
    }

    private static class LocalVolumeInfo {
        public String volId;
        public String state;
        public String uuid;

        LocalVolumeInfo(String line) {
            final String[] split = line.split(" ");
            volId = split[0];
            state = split[1];
            uuid = split[2];
        }
    }

    private LocalVolumeInfo getAdoptionVolume() throws Exception {
        String[] lines = null;
        int attempt = 0;
        int mounted_count = 0;
        while (attempt++ < 15) {
            lines = mTest.getDevice().executeShellCommand("sm list-volumes private").split("\n");
            LogUtil.CLog.w("getAdoptionVolume(): " + Arrays.toString(lines));
            for (String line : lines) {
                final LocalVolumeInfo info = new LocalVolumeInfo(line.trim());
                if (!"private".equals(info.volId)) {
                    if ("mounted".equals(info.state)) {
                        // make sure the storage is mounted and stable for a while
                        mounted_count++;
                        attempt--;
                        if (mounted_count >= 3) {
                            return waitForVolumeReady(info);
                        }
                    } else {
                        mounted_count = 0;
                    }
                }
            }
            Thread.sleep(1000);
        }
        throw new AssertionError("Expected private volume; found " + Arrays.toString(lines));
    }

    private LocalVolumeInfo waitForVolumeReady(LocalVolumeInfo vol) throws Exception {
        int attempt = 0;
        while (attempt++ < 15) {
            if (mTest.getDevice()
                    .executeShellCommand("dumpsys package volumes")
                    .contains(vol.volId)) {
                return vol;
            }
            Thread.sleep(1000);
        }
        throw new AssertionError("Volume not ready " + vol.volId);
    }
}