1/* 2 * Copyright (C) 2016 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 17package android.media; 18 19import java.lang.IllegalArgumentException; 20 21import android.annotation.NonNull; 22import android.app.ActivityThread; 23import android.app.AppOpsManager; 24import android.content.Context; 25import android.os.IBinder; 26import android.os.Process; 27import android.os.RemoteException; 28import android.os.ServiceManager; 29import android.util.Log; 30 31import com.android.internal.app.IAppOpsCallback; 32import com.android.internal.app.IAppOpsService; 33 34/** 35 * Class to encapsulate a number of common player operations: 36 * - AppOps for OP_PLAY_AUDIO 37 * - more to come (routing, transport control) 38 * @hide 39 */ 40public abstract class PlayerBase { 41 42 // parameters of the player that affect AppOps 43 protected AudioAttributes mAttributes; 44 protected float mLeftVolume = 1.0f; 45 protected float mRightVolume = 1.0f; 46 protected float mAuxEffectSendLevel = 0.0f; 47 48 // for AppOps 49 private final IAppOpsService mAppOps; 50 private final IAppOpsCallback mAppOpsCallback; 51 private boolean mHasAppOpsPlayAudio = true; 52 private final Object mAppOpsLock = new Object(); 53 54 55 /** 56 * Constructor. Must be given audio attributes, as they are required for AppOps. 57 * @param attr non-null audio attributes 58 */ 59 PlayerBase(@NonNull AudioAttributes attr) { 60 if (attr == null) { 61 throw new IllegalArgumentException("Illegal null AudioAttributes"); 62 } 63 mAttributes = attr; 64 IBinder b = ServiceManager.getService(Context.APP_OPS_SERVICE); 65 mAppOps = IAppOpsService.Stub.asInterface(b); 66 // initialize mHasAppOpsPlayAudio 67 updateAppOpsPlayAudio_sync(); 68 // register a callback to monitor whether the OP_PLAY_AUDIO is still allowed 69 mAppOpsCallback = new IAppOpsCallback.Stub() { 70 public void opChanged(int op, int uid, String packageName) { 71 synchronized (mAppOpsLock) { 72 if (op == AppOpsManager.OP_PLAY_AUDIO) { 73 updateAppOpsPlayAudio_sync(); 74 } 75 } 76 } 77 }; 78 try { 79 mAppOps.startWatchingMode(AppOpsManager.OP_PLAY_AUDIO, 80 ActivityThread.currentPackageName(), mAppOpsCallback); 81 } catch (RemoteException e) { 82 mHasAppOpsPlayAudio = false; 83 } 84 } 85 86 87 /** 88 * To be called whenever the audio attributes of the player change 89 * @param attr non-null audio attributes 90 */ 91 void baseUpdateAudioAttributes(@NonNull AudioAttributes attr) { 92 if (attr == null) { 93 throw new IllegalArgumentException("Illegal null AudioAttributes"); 94 } 95 synchronized (mAppOpsLock) { 96 mAttributes = attr; 97 updateAppOpsPlayAudio_sync(); 98 } 99 } 100 101 void baseStart() { 102 synchronized (mAppOpsLock) { 103 if (isRestricted_sync()) { 104 playerSetVolume(0, 0); 105 } 106 } 107 } 108 109 void baseSetVolume(float leftVolume, float rightVolume) { 110 synchronized (mAppOpsLock) { 111 mLeftVolume = leftVolume; 112 mRightVolume = rightVolume; 113 if (isRestricted_sync()) { 114 return; 115 } 116 } 117 playerSetVolume(leftVolume, rightVolume); 118 } 119 120 int baseSetAuxEffectSendLevel(float level) { 121 synchronized (mAppOpsLock) { 122 mAuxEffectSendLevel = level; 123 if (isRestricted_sync()) { 124 return AudioSystem.SUCCESS; 125 } 126 } 127 return playerSetAuxEffectSendLevel(level); 128 } 129 130 /** 131 * To be called from a subclass release or finalize method. 132 * Releases AppOps related resources. 133 */ 134 void baseRelease() { 135 try { 136 mAppOps.stopWatchingMode(mAppOpsCallback); 137 } catch (RemoteException e) { 138 // nothing to do here, the object is supposed to be released anyway 139 } 140 } 141 142 /** 143 * To be called whenever a condition that might affect audibility of this player is updated. 144 * Must be called synchronized on mAppOpsLock. 145 */ 146 void updateAppOpsPlayAudio_sync() { 147 boolean oldHasAppOpsPlayAudio = mHasAppOpsPlayAudio; 148 try { 149 final int mode = mAppOps.checkAudioOperation(AppOpsManager.OP_PLAY_AUDIO, 150 mAttributes.getUsage(), 151 Process.myUid(), ActivityThread.currentPackageName()); 152 mHasAppOpsPlayAudio = (mode == AppOpsManager.MODE_ALLOWED); 153 } catch (RemoteException e) { 154 mHasAppOpsPlayAudio = false; 155 } 156 157 // AppsOps alters a player's volume; when the restriction changes, reflect it on the actual 158 // volume used by the player 159 try { 160 if (oldHasAppOpsPlayAudio != mHasAppOpsPlayAudio) { 161 if (mHasAppOpsPlayAudio) { 162 playerSetVolume(mLeftVolume, mRightVolume); 163 playerSetAuxEffectSendLevel(mAuxEffectSendLevel); 164 } else { 165 playerSetVolume(0.0f, 0.0f); 166 playerSetAuxEffectSendLevel(0.0f); 167 } 168 } 169 } catch (Exception e) { 170 // failing silently, player might not be in right state 171 } 172 } 173 174 175 /** 176 * To be called by the subclass whenever an operation is potentially restricted. 177 * As the media player-common behavior are incorporated into this class, the subclass's need 178 * to call this method should be removed, and this method could become private. 179 * FIXME can this method be private so subclasses don't have to worry about when to check 180 * the restrictions. 181 * @return 182 */ 183 boolean isRestricted_sync() { 184 // check app ops 185 if (mHasAppOpsPlayAudio) { 186 return false; 187 } 188 // check bypass flag 189 if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) { 190 return false; 191 } 192 return true; 193 } 194 195 // Abstract methods a subclass needs to implement 196 abstract void playerSetVolume(float leftVolume, float rightVolume); 197 abstract int playerSetAuxEffectSendLevel(float level); 198} 199