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.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.ParcelFileDescriptor; 30 import android.util.Log; 31 32 import com.android.compatibility.common.util.AmUtils; 33 34 import junit.framework.Assert; 35 36 import java.io.FileInputStream; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.util.List; 40 import java.util.Scanner; 41 import java.util.concurrent.CountDownLatch; 42 import java.util.concurrent.TimeUnit; 43 44 public class Utils { 45 private static final String TAG = "CtsMediaTestUtil"; 46 private static final int TEST_TIMING_TOLERANCE_MS = 500; 47 private static final String MEDIA_PATH_INSTR_ARG_KEY = "media-path"; 48 enableAppOps(String packageName, String operation, Instrumentation instrumentation)49 public static void enableAppOps(String packageName, String operation, 50 Instrumentation instrumentation) { 51 setAppOps(packageName, operation, instrumentation, true); 52 } 53 disableAppOps(String packageName, String operation, Instrumentation instrumentation)54 public static void disableAppOps(String packageName, String operation, 55 Instrumentation instrumentation) { 56 setAppOps(packageName, operation, instrumentation, false); 57 } 58 convertStreamToString(InputStream is)59 public static String convertStreamToString(InputStream is) { 60 try (Scanner scanner = new Scanner(is).useDelimiter("\\A")) { 61 return scanner.hasNext() ? scanner.next() : ""; 62 } 63 } 64 setAppOps(String packageName, String operation, Instrumentation instrumentation, boolean enable)65 private static void setAppOps(String packageName, String operation, 66 Instrumentation instrumentation, boolean enable) { 67 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 68 StringBuilder cmd = new StringBuilder(); 69 cmd.append("appops set "); 70 cmd.append(packageName); 71 cmd.append(" "); 72 cmd.append(operation); 73 cmd.append(enable ? " allow" : " deny"); 74 try (InputStream inputStream = 75 new ParcelFileDescriptor.AutoCloseInputStream( 76 uiAutomation.executeShellCommand(cmd.toString()))) { 77 String result = convertStreamToString(inputStream); 78 if (!result.isEmpty()) { 79 Log.e(TAG, result); 80 return; 81 } 82 } catch (IOException e) { 83 Log.w(TAG, "Failure closing ParcelFileDescriptor"); 84 } 85 86 StringBuilder query = new StringBuilder(); 87 query.append("appops get "); 88 query.append(packageName); 89 query.append(" "); 90 query.append(operation); 91 String queryStr = query.toString(); 92 93 String expectedResult = enable ? "allow" : "deny"; 94 try (InputStream inputStream = 95 new ParcelFileDescriptor.AutoCloseInputStream( 96 uiAutomation.executeShellCommand(queryStr.toString()))) { 97 if (!convertStreamToString(inputStream).contains(expectedResult)) { 98 Log.w(TAG, "setAppOps did not return " + expectedResult); 99 } 100 } catch (IOException e) { 101 Log.w(TAG, "Failure closing ParcelFileDescriptor"); 102 } 103 } 104 toggleNotificationPolicyAccess(String packageName, Instrumentation instrumentation, boolean on)105 public static void toggleNotificationPolicyAccess(String packageName, 106 Instrumentation instrumentation, boolean on) throws IOException { 107 108 String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName; 109 110 // Get permission to enable accessibility 111 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 112 // Execute command 113 try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) { 114 Assert.assertNotNull("Failed to execute shell command: " + command, fd); 115 // Wait for the command to finish by reading until EOF 116 try (InputStream in = new FileInputStream(fd.getFileDescriptor())) { 117 byte[] buffer = new byte[4096]; 118 while (in.read(buffer) > 0) {} 119 } catch (IOException e) { 120 throw new IOException("Could not read stdout of command: " + command, e); 121 } 122 } finally { 123 uiAutomation.destroy(); 124 } 125 126 AmUtils.waitForBroadcastBarrier(); 127 128 NotificationManager nm = (NotificationManager) instrumentation.getContext() 129 .getSystemService(Context.NOTIFICATION_SERVICE); 130 Assert.assertEquals("Notification Policy Access Grant is " 131 + nm.isNotificationPolicyAccessGranted() + " not " + on + " for " 132 + packageName, on, nm.isNotificationPolicyAccessGranted()); 133 } 134 compareRemoteUserInfo(RemoteUserInfo a, RemoteUserInfo b)135 public 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 public 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