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 17 package android.media; 18 19 import java.lang.IllegalArgumentException; 20 21 import android.annotation.NonNull; 22 import android.app.ActivityThread; 23 import android.app.AppOpsManager; 24 import android.content.Context; 25 import android.os.IBinder; 26 import android.os.Process; 27 import android.os.RemoteException; 28 import android.os.ServiceManager; 29 import android.util.Log; 30 31 import com.android.internal.app.IAppOpsCallback; 32 import 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 */ 40 public 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 */ PlayerBase(@onNull AudioAttributes attr)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 */ baseUpdateAudioAttributes(@onNull AudioAttributes attr)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 baseStart()101 void baseStart() { 102 synchronized (mAppOpsLock) { 103 if (isRestricted_sync()) { 104 playerSetVolume(0, 0); 105 } 106 } 107 } 108 baseSetVolume(float leftVolume, float rightVolume)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 baseSetAuxEffectSendLevel(float level)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 */ baseRelease()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 */ updateAppOpsPlayAudio_sync()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 */ isRestricted_sync()183 boolean isRestricted_sync() { 184 if ((mAttributes.getAllFlags() & AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY) != 0) { 185 return false; 186 } 187 return !mHasAppOpsPlayAudio; 188 } 189 190 // Abstract methods a subclass needs to implement playerSetVolume(float leftVolume, float rightVolume)191 abstract void playerSetVolume(float leftVolume, float rightVolume); playerSetAuxEffectSendLevel(float level)192 abstract int playerSetAuxEffectSendLevel(float level); 193 } 194