• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.providers.media.photopicker;
18 
19 import static android.Manifest.permission.READ_DEVICE_CONFIG;
20 import static android.Manifest.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG;
21 import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
22 
23 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
24 
25 import static com.android.providers.media.ConfigStore.ConfigStoreImpl.KEY_CLOUD_MEDIA_FEATURE_ENABLED;
26 import static com.android.providers.media.ConfigStore.ConfigStoreImpl.KEY_CLOUD_MEDIA_PROVIDER_ALLOWLIST;
27 
28 import static com.google.common.truth.Truth.assertWithMessage;
29 
30 import android.os.UserHandle;
31 import android.provider.DeviceConfig;
32 import android.text.TextUtils;
33 import android.util.Log;
34 
35 import androidx.annotation.NonNull;
36 import androidx.annotation.Nullable;
37 import androidx.test.uiautomator.UiDevice;
38 
39 import com.android.modules.utils.build.SdkLevel;
40 
41 import java.io.IOException;
42 
43 public class PhotoPickerCloudTestUtils {
44     private static final String TAG = PhotoPickerCloudTestUtils.class.getSimpleName();
45     static final String INVALID_CLOUD_PROVIDER = "Invalid";
46 
47     /**
48      * @return true if cloud is enabled in the device config of the given namespace,
49      * otherwise false.
50      */
isCloudMediaEnabled(@onNull String namespace)51     static boolean isCloudMediaEnabled(@NonNull String namespace) {
52         return Boolean.parseBoolean(
53                 readDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_FEATURE_ENABLED));
54     }
55 
56     /**
57      * @return the allowed providers in the device config of the given namespace.
58      */
59     @Nullable
getAllowedProvidersDeviceConfig(@onNull String namespace)60     static String getAllowedProvidersDeviceConfig(@NonNull String namespace) {
61         return readDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_PROVIDER_ALLOWLIST);
62     }
63 
64     /**
65      * Enables cloud media and sets the allowed cloud provider in the given namespace.
66      */
enableCloudMediaAndSetAllowedCloudProviders( @onNull String namespace, @NonNull String allowedPackagesJoined)67     public static void enableCloudMediaAndSetAllowedCloudProviders(
68             @NonNull String namespace, @NonNull String allowedPackagesJoined) {
69         writeDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_PROVIDER_ALLOWLIST, allowedPackagesJoined);
70         assertWithMessage("Failed to update the allowed cloud providers device config")
71                 .that(getAllowedProvidersDeviceConfig(namespace))
72                 .isEqualTo(allowedPackagesJoined);
73 
74         writeDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_FEATURE_ENABLED, true);
75         assertWithMessage("Failed to update the cloud media feature device config")
76                 .that(isCloudMediaEnabled(namespace))
77                 .isTrue();
78     }
79 
80     /**
81      * Disable cloud media in the given namespace.
82      */
disableCloudMediaAndClearAllowedCloudProviders(@onNull String namespace)83     static void disableCloudMediaAndClearAllowedCloudProviders(@NonNull String namespace) {
84         writeDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_FEATURE_ENABLED, false);
85         assertWithMessage("Failed to update the cloud media feature device config")
86                 .that(isCloudMediaEnabled(namespace))
87                 .isFalse();
88 
89         deleteDeviceConfigProp(namespace, KEY_CLOUD_MEDIA_PROVIDER_ALLOWLIST);
90         assertWithMessage("Failed to delete the allowed cloud providers device config")
91                 .that(getAllowedProvidersDeviceConfig(namespace))
92                 .isNull();
93     }
94 
95     @Nullable
readDeviceConfigProp(@onNull String namespace, @NonNull String name)96     private static String readDeviceConfigProp(@NonNull String namespace, @NonNull String name) {
97         getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(READ_DEVICE_CONFIG);
98         try {
99             return DeviceConfig.getProperty(namespace, name);
100         } finally {
101             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
102         }
103     }
104 
writeDeviceConfigProp( @onNull String namespace, @NonNull String key, boolean value)105     private static void writeDeviceConfigProp(
106             @NonNull String namespace, @NonNull String key, boolean value) {
107         writeDeviceConfigProp(namespace, key, Boolean.toString(value));
108     }
109 
writeDeviceConfigProp( @onNull String namespace, @NonNull String name, @NonNull String value)110     private static void writeDeviceConfigProp(
111             @NonNull String namespace, @NonNull String name, @NonNull String value) {
112         getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG,
113                 WRITE_ALLOWLISTED_DEVICE_CONFIG);
114 
115         try {
116             DeviceConfig.setProperty(namespace, name, value, /* makeDefault= */ false);
117         } finally {
118             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
119         }
120     }
121 
deleteDeviceConfigProp(@onNull String namespace, @NonNull String name)122     private static void deleteDeviceConfigProp(@NonNull String namespace, @NonNull String name) {
123         getInstrumentation().getUiAutomation().adoptShellPermissionIdentity(WRITE_DEVICE_CONFIG,
124                 WRITE_ALLOWLISTED_DEVICE_CONFIG);
125         try {
126             if (SdkLevel.isAtLeastU()) {
127                 DeviceConfig.deleteProperty(namespace, name);
128             } else {
129                 // DeviceConfig.deleteProperty API is only available from U onwards.
130                 try {
131                     UiDevice.getInstance(getInstrumentation())
132                             .executeShellCommand(
133                                     String.format("device_config delete %s %s", namespace, name));
134                 } catch (IOException e) {
135                     Log.e(TAG, String.format("Could not delete device_config %s / %s",
136                             namespace, name), e);
137                 }
138             }
139         } finally {
140             getInstrumentation().getUiAutomation().dropShellPermissionIdentity();
141         }
142     }
143 
144     /**
145      * Set cloud provider in the device to the provided authority.
146      * If the provided cloud authority equals {@link #INVALID_CLOUD_PROVIDER},
147      * the cloud provider will not be updated.
148      */
setCloudProvider(@onNull UiDevice sDevice, @Nullable String authority)149     public static void setCloudProvider(@NonNull UiDevice sDevice,
150             @Nullable String authority) throws IOException {
151         if (INVALID_CLOUD_PROVIDER.equals(authority)) {
152             Log.w(TAG, "Cloud provider is invalid. "
153                     + "Ignoring the request to set the cloud provider to invalid");
154             return;
155         }
156         if (authority == null) {
157             sDevice.executeShellCommand(
158                     "content call"
159                             + " --user " + UserHandle.myUserId()
160                             + " --uri content://media/"
161                             + " --method set_cloud_provider"
162                             + " --extra cloud_provider:n:null");
163         } else {
164             sDevice.executeShellCommand(
165                     "content call"
166                             + " --user " + UserHandle.myUserId()
167                             + " --uri content://media/"
168                             + " --method set_cloud_provider"
169                             + " --extra cloud_provider:s:" + authority);
170         }
171     }
172 
173     /**
174      * @return the current cloud provider.
175      */
getCurrentCloudProvider(UiDevice sDevice)176     public static String getCurrentCloudProvider(UiDevice sDevice) throws IOException {
177         final String shellOutput =
178                 sDevice.executeShellCommand(
179                         "content call"
180                                 + " --user " + UserHandle.myUserId()
181                                 + " --uri content://media/"
182                                 + " --method get_cloud_provider");
183         return extractCloudProvider(shellOutput);
184     }
185 
extractCloudProvider(String shellOutput)186     private static String extractCloudProvider(String shellOutput) {
187         final String[] splitOutput;
188         if (TextUtils.isEmpty(shellOutput) || ((splitOutput = shellOutput.split("=")).length < 2)) {
189             throw new RuntimeException("Could not get current cloud provider. Output: "
190                     + shellOutput);
191         }
192         String cloudProvider = splitOutput[1];
193         cloudProvider = cloudProvider.substring(0, cloudProvider.length() - 3);
194         if (cloudProvider.equals("null")) {
195             return null;
196         }
197         return cloudProvider;
198     }
199 }
200