1 /* 2 * Copyright (C) 2019 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 com.android.server.appop; 18 19 import android.app.AppOpsManager; 20 import android.hardware.camera2.CameraDevice; 21 import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; 22 import android.media.AudioAttributes; 23 import android.util.ArraySet; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 import android.util.SparseBooleanArray; 27 28 import java.io.PrintWriter; 29 30 /** 31 * AudioRestrictionManager host all audio restriction related logic and states for AppOpsService. 32 */ 33 public class AudioRestrictionManager { 34 static final String TAG = "AudioRestriction"; 35 36 // Audio restrictions coming from Zen mode API 37 final SparseArray<SparseArray<Restriction>> mZenModeAudioRestrictions = new SparseArray<>(); 38 // Audio restrictions coming from Camera2 API 39 @CAMERA_AUDIO_RESTRICTION int mCameraAudioRestriction = CameraDevice.AUDIO_RESTRICTION_NONE; 40 // Predefined <code, usages> camera audio restriction settings 41 static final SparseArray<SparseBooleanArray> CAMERA_AUDIO_RESTRICTIONS; 42 43 static { 44 SparseBooleanArray audioMutedUsages = new SparseBooleanArray(); 45 SparseBooleanArray vibrationMutedUsages = new SparseBooleanArray(); 46 for (int usage : AudioAttributes.SDK_USAGES.toArray()) { 47 final int suppressionBehavior = AudioAttributes.SUPPRESSIBLE_USAGES.get(usage); 48 if (suppressionBehavior == AudioAttributes.SUPPRESSIBLE_NOTIFICATION || 49 suppressionBehavior == AudioAttributes.SUPPRESSIBLE_CALL || 50 suppressionBehavior == AudioAttributes.SUPPRESSIBLE_ALARM) { audioMutedUsages.append(usage, true)51 audioMutedUsages.append(usage, true); vibrationMutedUsages.append(usage, true)52 vibrationMutedUsages.append(usage, true); 53 } else if (suppressionBehavior != AudioAttributes.SUPPRESSIBLE_MEDIA && 54 suppressionBehavior != AudioAttributes.SUPPRESSIBLE_SYSTEM && 55 suppressionBehavior != AudioAttributes.SUPPRESSIBLE_NEVER) { Slog.e(TAG, "Unknown audio suppression behavior" + suppressionBehavior)56 Slog.e(TAG, "Unknown audio suppression behavior" + suppressionBehavior); 57 } 58 } 59 CAMERA_AUDIO_RESTRICTIONS = new SparseArray<>(); CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_PLAY_AUDIO, audioMutedUsages)60 CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_PLAY_AUDIO, audioMutedUsages); CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_VIBRATE, vibrationMutedUsages)61 CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_VIBRATE, vibrationMutedUsages); 62 } 63 64 private static final class Restriction { 65 private static final ArraySet<String> NO_EXCEPTIONS = new ArraySet<String>(); 66 int mode; 67 ArraySet<String> exceptionPackages = NO_EXCEPTIONS; 68 } 69 checkAudioOperation(int code, int usage, int uid, String packageName)70 public int checkAudioOperation(int code, int usage, int uid, String packageName) { 71 synchronized (this) { 72 // Check for camera audio restrictions 73 if (mCameraAudioRestriction != CameraDevice.AUDIO_RESTRICTION_NONE) { 74 if (code == AppOpsManager.OP_VIBRATE || (code == AppOpsManager.OP_PLAY_AUDIO && 75 mCameraAudioRestriction == 76 CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND)) { 77 final SparseBooleanArray mutedUsages = CAMERA_AUDIO_RESTRICTIONS.get(code); 78 if (mutedUsages != null) { 79 if (mutedUsages.get(usage)) { 80 return AppOpsManager.MODE_IGNORED; 81 } 82 } 83 } 84 } 85 86 final int mode = checkZenModeRestrictionLocked(code, usage, uid, packageName); 87 if (mode != AppOpsManager.MODE_ALLOWED) { 88 return mode; 89 } 90 } 91 return AppOpsManager.MODE_ALLOWED; 92 } 93 checkZenModeRestrictionLocked(int code, int usage, int uid, String packageName)94 private int checkZenModeRestrictionLocked(int code, int usage, int uid, String packageName) { 95 final SparseArray<Restriction> usageRestrictions = mZenModeAudioRestrictions.get(code); 96 if (usageRestrictions != null) { 97 final Restriction r = usageRestrictions.get(usage); 98 if (r != null && !r.exceptionPackages.contains(packageName)) { 99 return r.mode; 100 } 101 } 102 return AppOpsManager.MODE_ALLOWED; 103 } 104 setZenModeAudioRestriction(int code, int usage, int uid, int mode, String[] exceptionPackages)105 public void setZenModeAudioRestriction(int code, int usage, int uid, int mode, 106 String[] exceptionPackages) { 107 synchronized (this) { 108 SparseArray<Restriction> usageRestrictions = mZenModeAudioRestrictions.get(code); 109 if (usageRestrictions == null) { 110 usageRestrictions = new SparseArray<Restriction>(); 111 mZenModeAudioRestrictions.put(code, usageRestrictions); 112 } 113 usageRestrictions.remove(usage); 114 if (mode != AppOpsManager.MODE_ALLOWED) { 115 final Restriction r = new Restriction(); 116 r.mode = mode; 117 if (exceptionPackages != null) { 118 final int N = exceptionPackages.length; 119 r.exceptionPackages = new ArraySet<String>(N); 120 for (int i = 0; i < N; i++) { 121 final String pkg = exceptionPackages[i]; 122 if (pkg != null) { 123 r.exceptionPackages.add(pkg.trim()); 124 } 125 } 126 } 127 usageRestrictions.put(usage, r); 128 } 129 } 130 } 131 setCameraAudioRestriction(@AMERA_AUDIO_RESTRICTION int mode)132 public void setCameraAudioRestriction(@CAMERA_AUDIO_RESTRICTION int mode) { 133 synchronized (this) { 134 mCameraAudioRestriction = mode; 135 } 136 } 137 hasActiveRestrictions()138 public boolean hasActiveRestrictions() { 139 boolean hasActiveRestrictions = false; 140 synchronized (this) { 141 hasActiveRestrictions = (mZenModeAudioRestrictions.size() > 0 || 142 mCameraAudioRestriction != CameraDevice.AUDIO_RESTRICTION_NONE); 143 } 144 return hasActiveRestrictions; 145 } 146 147 // return: needSep used by AppOpsService#dump dump(PrintWriter pw)148 public boolean dump(PrintWriter pw) { 149 boolean printedHeader = false; 150 boolean needSep = hasActiveRestrictions(); 151 152 synchronized (this) { 153 for (int o = 0; o < mZenModeAudioRestrictions.size(); o++) { 154 final String op = AppOpsManager.opToName(mZenModeAudioRestrictions.keyAt(o)); 155 final SparseArray<Restriction> restrictions = mZenModeAudioRestrictions.valueAt(o); 156 for (int i = 0; i < restrictions.size(); i++) { 157 if (!printedHeader){ 158 pw.println(" Zen Mode Audio Restrictions:"); 159 printedHeader = true; 160 161 } 162 final int usage = restrictions.keyAt(i); 163 pw.print(" "); pw.print(op); 164 pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage)); 165 Restriction r = restrictions.valueAt(i); 166 pw.print(": mode="); pw.println(AppOpsManager.modeToName(r.mode)); 167 if (!r.exceptionPackages.isEmpty()) { 168 pw.println(" Exceptions:"); 169 for (int j = 0; j < r.exceptionPackages.size(); j++) { 170 pw.print(" "); pw.println(r.exceptionPackages.valueAt(j)); 171 } 172 } 173 } 174 } 175 if (mCameraAudioRestriction != CameraDevice.AUDIO_RESTRICTION_NONE) { 176 pw.println(" Camera Audio Restriction Mode: " + 177 cameraRestrictionModeToName(mCameraAudioRestriction)); 178 } 179 } 180 return needSep; 181 } 182 cameraRestrictionModeToName(@AMERA_AUDIO_RESTRICTION int mode)183 private static String cameraRestrictionModeToName(@CAMERA_AUDIO_RESTRICTION int mode) { 184 switch (mode) { 185 case CameraDevice.AUDIO_RESTRICTION_NONE: 186 return "None"; 187 case CameraDevice.AUDIO_RESTRICTION_VIBRATION: 188 return "MuteVibration"; 189 case CameraDevice.AUDIO_RESTRICTION_VIBRATION_SOUND: 190 return "MuteVibrationAndSound"; 191 default: 192 return "Unknown"; 193 } 194 } 195 196 } 197