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.media.AudioManager; 24 import android.media.AudioPlaybackConfiguration; 25 import android.media.MediaPlayer; 26 import android.media.session.MediaSessionManager.RemoteUserInfo; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.os.Handler; 30 import android.os.HandlerThread; 31 import android.os.ParcelFileDescriptor; 32 import android.util.Log; 33 import androidx.test.platform.app.InstrumentationRegistry; 34 import java.io.File; 35 import java.io.FileInputStream; 36 import java.io.IOException; 37 import java.io.InputStream; 38 import java.util.List; 39 import java.util.Scanner; 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 43 import junit.framework.Assert; 44 45 public class Utils { 46 private static final String TAG = "CtsMediaTestUtil"; 47 private static final int TEST_TIMING_TOLERANCE_MS = 500; 48 private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path"; 49 enableAppOps(String packageName, String operation, Instrumentation instrumentation)50 public static void enableAppOps(String packageName, String operation, 51 Instrumentation instrumentation) { 52 setAppOps(packageName, operation, instrumentation, true); 53 } 54 disableAppOps(String packageName, String operation, Instrumentation instrumentation)55 public static void disableAppOps(String packageName, String operation, 56 Instrumentation instrumentation) { 57 setAppOps(packageName, operation, instrumentation, false); 58 } 59 convertStreamToString(InputStream is)60 public static String convertStreamToString(InputStream is) { 61 try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) { 62 return scanner.hasNext() ? scanner.next() : ""; 63 } 64 } 65 getMediaPath()66 public static String getMediaPath() { 67 Bundle bundle = InstrumentationRegistry.getArguments(); 68 String mediaPath = bundle.getString(MEDIA_PATH_INSTR_ARG_KEY); 69 Log.i(TAG, "Media Path value is: " + mediaPath); 70 71 if (mediaPath != null && !mediaPath.isEmpty()) { 72 if (mediaPath.startsWith("http") || mediaPath.startsWith("file")) { 73 return mediaPath; 74 } 75 // Otherwise, assume a file path that is not already Uri formatted 76 return Uri.fromFile(new File(mediaPath)).toString(); 77 } 78 return "https://storage.googleapis.com/wvmedia"; 79 } 80 setAppOps(String packageName, String operation, Instrumentation instrumentation, boolean enable)81 private static void setAppOps(String packageName, String operation, 82 Instrumentation instrumentation, boolean enable) { 83 StringBuilder cmd = new StringBuilder(); 84 cmd.append("appops set "); 85 cmd.append(packageName); 86 cmd.append(" "); 87 cmd.append(operation); 88 cmd.append(enable ? " allow" : " deny"); 89 instrumentation.getUiAutomation().executeShellCommand(cmd.toString()); 90 91 StringBuilder query = new StringBuilder(); 92 query.append("appops get "); 93 query.append(packageName); 94 query.append(" "); 95 query.append(operation); 96 String queryStr = query.toString(); 97 98 String expectedResult = enable ? "allow" : "deny"; 99 String result = ""; 100 while(!result.contains(expectedResult)) { 101 ParcelFileDescriptor pfd = instrumentation.getUiAutomation().executeShellCommand( 102 queryStr); 103 InputStream inputStream = new FileInputStream(pfd.getFileDescriptor()); 104 result = convertStreamToString(inputStream); 105 } 106 } 107 toggleNotificationPolicyAccess(String packageName, Instrumentation instrumentation, boolean on)108 protected static void toggleNotificationPolicyAccess(String packageName, 109 Instrumentation instrumentation, boolean on) throws IOException { 110 111 String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName; 112 113 // Get permission to enable accessibility 114 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 115 // Execute command 116 try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) { 117 Assert.assertNotNull("Failed to execute shell command: " + command, fd); 118 // Wait for the command to finish by reading until EOF 119 try (InputStream in = new FileInputStream(fd.getFileDescriptor())) { 120 byte[] buffer = new byte[4096]; 121 while (in.read(buffer) > 0) {} 122 } catch (IOException e) { 123 throw new IOException("Could not read stdout of command: " + command, e); 124 } 125 } finally { 126 uiAutomation.destroy(); 127 } 128 129 NotificationManager nm = (NotificationManager) instrumentation.getContext() 130 .getSystemService(Context.NOTIFICATION_SERVICE); 131 Assert.assertEquals("Wrote setting should be the same as the read one", on, 132 nm.isNotificationPolicyAccessGranted()); 133 } 134 compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b)135 static boolean compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b) { 136 if (a == null && b == null) { 137 return true; 138 } else if (a == null || b == null) { 139 return false; 140 } 141 return a.getPackageName().equals(b.getPackageName()) 142 && a.getPid() == b.getPid() 143 && a.getUid() == b.getUid(); 144 } 145 146 /** 147 * Assert that a media playback is started and an active {@link AudioPlaybackConfiguration} 148 * is created once. The playback will be stopped immediately after that. 149 * <p>For a media session to receive media button events, an actual playback is needed. 150 */ assertMediaPlaybackStarted(Context context)151 static void assertMediaPlaybackStarted(Context context) { 152 final AudioManager am = new AudioManager(context); 153 final HandlerThread handlerThread = new HandlerThread(TAG); 154 handlerThread.start(); 155 final TestAudioPlaybackCallback callback = new TestAudioPlaybackCallback(); 156 MediaPlayer mediaPlayer = null; 157 158 try { 159 final int activeConfigSizeBeforeStart = am.getActivePlaybackConfigurations().size(); 160 final Handler handler = new Handler(handlerThread.getLooper()); 161 162 am.registerAudioPlaybackCallback(callback, handler); 163 mediaPlayer = MediaPlayer.create(context, R.raw.sine1khzs40dblong); 164 mediaPlayer.start(); 165 if (!callback.mCountDownLatch.await(TEST_TIMING_TOLERANCE_MS, TimeUnit.MILLISECONDS) 166 || callback.mActiveConfigSize != activeConfigSizeBeforeStart + 1) { 167 Assert.fail("Failed to create an active AudioPlaybackConfiguration"); 168 } 169 } catch (InterruptedException e) { 170 Assert.fail("Failed to create an active AudioPlaybackConfiguration"); 171 } finally { 172 am.unregisterAudioPlaybackCallback(callback); 173 if (mediaPlayer != null) { 174 mediaPlayer.stop(); 175 mediaPlayer.release(); 176 mediaPlayer = null; 177 } 178 handlerThread.quitSafely(); 179 } 180 } 181 182 private static class TestAudioPlaybackCallback extends AudioManager.AudioPlaybackCallback { 183 private final CountDownLatch mCountDownLatch = new CountDownLatch(1); 184 private int mActiveConfigSize; 185 186 @Override onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs)187 public void onPlaybackConfigChanged(List<AudioPlaybackConfiguration> configs) { 188 // For non-framework apps, only anonymized active AudioPlaybackCallbacks will be 189 // notified. 190 mActiveConfigSize = configs.size(); 191 mCountDownLatch.countDown(); 192 } 193 } 194 } 195