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