Home | History | Annotate | Download | only in cts
      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 
     50     public static void enableAppOps(String packageName, String operation,
     51             Instrumentation instrumentation) {
     52         setAppOps(packageName, operation, instrumentation, true);
     53     }
     54 
     55     public static void disableAppOps(String packageName, String operation,
     56             Instrumentation instrumentation) {
     57         setAppOps(packageName, operation, instrumentation, false);
     58     }
     59 
     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 
     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 
     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 
    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 
    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      */
    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
    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