• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.media.cts;
18 
19 import android.app.Instrumentation;
20 import android.app.NotificationManager;
21 import android.app.UiAutomation;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.media.AudioManager;
25 import android.media.AudioPlaybackConfiguration;
26 import android.media.MediaPlayer;
27 import android.media.session.MediaSessionManager.RemoteUserInfo;
28 import android.net.Uri;
29 import android.os.Handler;
30 import android.os.HandlerThread;
31 import android.os.ParcelFileDescriptor;
32 import android.os.UserHandle;
33 import android.util.Log;
34 
35 import com.android.compatibility.common.util.AmUtils;
36 
37 import junit.framework.Assert;
38 
39 import java.io.File;
40 import java.io.FileInputStream;
41 import java.io.FileWriter;
42 import java.io.IOException;
43 import java.io.InputStream;
44 import java.util.List;
45 import java.util.Scanner;
46 import java.util.concurrent.CountDownLatch;
47 import java.util.concurrent.TimeUnit;
48 
49 public class Utils {
50     private static final String TAG = "CtsMediaTestUtil";
51     private static final int TEST_TIMING_TOLERANCE_MS = 500;
52     private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path";
53 
54     public static final Uri RINGTONE_TEST_URI = Uri.parse("content://cts/ringtone/");
55 
enableAppOps(String packageName, String operation, Instrumentation instrumentation)56     public static void enableAppOps(String packageName, String operation,
57             Instrumentation instrumentation) {
58         setAppOps(packageName, operation, instrumentation, true);
59     }
60 
disableAppOps(String packageName, String operation, Instrumentation instrumentation)61     public static void disableAppOps(String packageName, String operation,
62             Instrumentation instrumentation) {
63         setAppOps(packageName, operation, instrumentation, false);
64     }
65 
convertStreamToString(InputStream is)66     public static String convertStreamToString(InputStream is) {
67         try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) {
68             return scanner.hasNext() ? scanner.next() : "";
69         }
70     }
71 
setAppOps(String packageName, String operation, Instrumentation instrumentation, boolean enable)72     private static void setAppOps(String packageName, String operation,
73             Instrumentation instrumentation, boolean enable) {
74         UiAutomation uiAutomation = instrumentation.getUiAutomation();
75         StringBuilder cmd = new StringBuilder();
76         cmd.append("appops set --user ");
77         cmd.append(UserHandle.myUserId());
78         cmd.append(" ");
79         cmd.append(packageName);
80         cmd.append(" ");
81         cmd.append(operation);
82         cmd.append(enable ? " allow" : " deny");
83         try (InputStream inputStream =
84                 new ParcelFileDescriptor.AutoCloseInputStream(
85                         uiAutomation.executeShellCommand(cmd.toString()))) {
86             String result = convertStreamToString(inputStream);
87             if (!result.isEmpty()) {
88                 Log.e(TAG, result);
89                 return;
90             }
91         } catch (IOException e) {
92             Log.w(TAG, "Failure closing ParcelFileDescriptor");
93         }
94 
95         StringBuilder query = new StringBuilder();
96         query.append("appops get ");
97         query.append(packageName);
98         query.append(" ");
99         query.append(operation);
100         String queryStr = query.toString();
101 
102         String expectedResult = enable ? "allow" : "deny";
103         try (InputStream inputStream =
104                 new ParcelFileDescriptor.AutoCloseInputStream(
105                         uiAutomation.executeShellCommand(queryStr.toString()))) {
106             if (!convertStreamToString(inputStream).contains(expectedResult)) {
107                 Log.w(TAG, "setAppOps did not return " + expectedResult);
108             }
109         } catch (IOException e) {
110             Log.w(TAG, "Failure closing ParcelFileDescriptor");
111         }
112     }
113 
toggleNotificationPolicyAccess(String packageName, Instrumentation instrumentation, boolean on)114     public static void toggleNotificationPolicyAccess(String packageName,
115             Instrumentation instrumentation, boolean on) throws IOException {
116 
117         int userId = instrumentation.getTargetContext().getUserId();
118         String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName
119                 + " " + userId;
120 
121         // Get permission to enable accessibility
122         UiAutomation uiAutomation = instrumentation.getUiAutomation();
123         // Execute command
124         try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) {
125             Assert.assertNotNull("Failed to execute shell command: " + command, fd);
126             // Wait for the command to finish by reading until EOF
127             try (InputStream in = new FileInputStream(fd.getFileDescriptor())) {
128                 byte[] buffer = new byte[4096];
129                 while (in.read(buffer) > 0) {}
130             } catch (IOException e) {
131                 throw new IOException("Could not read stdout of command: " + command, e);
132             }
133         } finally {
134             uiAutomation.destroy();
135         }
136 
137         AmUtils.waitForBroadcastBarrier();
138 
139         NotificationManager nm = (NotificationManager) instrumentation.getContext()
140                 .getSystemService(Context.NOTIFICATION_SERVICE);
141         Assert.assertEquals("Notification Policy Access Grant is "
142                 + nm.isNotificationPolicyAccessGranted() + " not " + on + " for "
143                 + packageName, on, nm.isNotificationPolicyAccessGranted());
144     }
145 
compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b)146     public static boolean compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b) {
147         if (a == null && b == null) {
148             return true;
149         } else if (a == null || b == null) {
150             return false;
151         }
152         return a.getPackageName().equals(b.getPackageName())
153                 && a.getPid() == b.getPid()
154                 && a.getUid() == b.getUid();
155     }
156 
157     /**
158      * Assert that a media playback is started and an active {@link AudioPlaybackConfiguration}
159      * is created once. The playback will be stopped immediately after that.
160      * <p>For a media session to receive media button events, an actual playback is needed.
161      */
assertMediaPlaybackStarted(Context context)162     public static void assertMediaPlaybackStarted(Context context) {
163         final AudioManager am = new AudioManager(context);
164         final HandlerThread handlerThread = new HandlerThread(TAG);
165         handlerThread.start();
166         final TestAudioPlaybackCallback callback = new TestAudioPlaybackCallback();
167         MediaPlayer mediaPlayer = null;
168 
169         try {
170             final int activeConfigSizeBeforeStart = am.getActivePlaybackConfigurations().size();
171             final Handler handler = new Handler(handlerThread.getLooper());
172 
173             am.registerAudioPlaybackCallback(callback, handler);
174             mediaPlayer = MediaPlayer.create(context, R.raw.sine1khzs40dblong);
175             mediaPlayer.start();
176             if (!callback.mCountDownLatch.await(TEST_TIMING_TOLERANCE_MS, TimeUnit.MILLISECONDS)
177                     || callback.mActiveConfigSize != activeConfigSizeBeforeStart + 1) {
178                 Assert.fail("Failed to create an active AudioPlaybackConfiguration");
179             }
180         } catch (InterruptedException e) {
181             Assert.fail("Failed to create an active AudioPlaybackConfiguration");
182         } finally {
183             am.unregisterAudioPlaybackCallback(callback);
184             if (mediaPlayer != null) {
185                 mediaPlayer.stop();
186                 mediaPlayer.release();
187                 mediaPlayer = null;
188             }
189             handlerThread.quitSafely();
190         }
191     }
192 
193     /**
194      * Gets the {@link com.android.internal.R.bool#config_ringtoneVibrationSettingsSupported} value.
195      * @return {@code true} If the device supports ringtone vibration settings.
196      */
isRingtoneVibrationSupported(Context context)197     public static boolean isRingtoneVibrationSupported(Context context) {
198         try {
199             int resId = Resources.getSystem().getIdentifier(
200                     "config_ringtoneVibrationSettingsSupported", "bool", "android");
201             return context.getResources().getBoolean(resId);
202         } catch (Resources.NotFoundException e) {
203             Log.w(TAG, "Unable to read system resource " + e.getMessage());
204             return false;
205         }
206     }
207 
208     /**
209      * Returns a temp file which includes predefined the {@link android.os.VibrationEffect}
210      * information.
211      */
getTestVibrationFile()212     public static File getTestVibrationFile() throws IOException {
213         File tempFile = File.createTempFile("test_vibration_file", ".xml");
214         FileWriter writer = new FileWriter(tempFile);
215         writer.write("<vibration-effect>\n"
216                 + "    <waveform-effect>\n"
217                 + "        <!-- PRIMING -->\n"
218                 + "        <waveform-entry durationMs=\"0\" amplitude=\"0\"/>\n"
219                 + "        <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
220                 + "        <waveform-entry durationMs=\"250\" amplitude=\"100\"/>\n"
221                 + "        <waveform-entry durationMs=\"12\" amplitude=\"255\"/>\n"
222                 + "        <waveform-entry durationMs=\"500\" amplitude=\"100\"/>\n"
223                 + "    </waveform-effect>\n"
224                 + "</vibration-effect>");
225         writer.close();
226         return tempFile;
227     }
228 
229     private static class TestAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback {
230         private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
231         private int mActiveConfigSize;
232 
233         @Override
onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs)234         public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) {
235             // For non-framework apps, only anonymized active AudioPlaybackCallbacks will be
236             // notified.
237             mActiveConfigSize = configs.size();
238             mCountDownLatch.countDown();
239         }
240     }
241 }
242