blob: be870373af63457b758d3c6d66494011ac1976b1 [file] [log] [blame]
Yin-Chia Yeh51d85162019-08-06 15:31:39 -07001/*
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
17package com.android.server.appop;
18
19import android.app.AppOpsManager;
20import android.hardware.camera2.CameraDevice;
21import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION;
22import android.media.AudioAttributes;
23import android.util.ArraySet;
24import android.util.Slog;
25import android.util.SparseArray;
26import android.util.SparseBooleanArray;
27
28import java.io.PrintWriter;
29
30/**
31 * AudioRestrictionManager host all audio restriction related logic and states for AppOpsService.
32 */
33public 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) {
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) {
51 audioMutedUsages.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) {
56 Slog.e(TAG, "Unknown audio suppression behavior" + suppressionBehavior);
57 }
58 }
59 CAMERA_AUDIO_RESTRICTIONS = new SparseArray<>();
60 CAMERA_AUDIO_RESTRICTIONS.append(AppOpsManager.OP_PLAY_AUDIO, audioMutedUsages);
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
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
Yin-Chia Yeh3eb79d62019-08-19 15:55:34 -070086 final int mode = checkZenModeRestrictionLocked(code, usage, uid, packageName);
Yin-Chia Yeh51d85162019-08-06 15:31:39 -070087 if (mode != AppOpsManager.MODE_ALLOWED) {
88 return mode;
89 }
90 }
91 return AppOpsManager.MODE_ALLOWED;
92 }
93
Yin-Chia Yeh3eb79d62019-08-19 15:55:34 -070094 private int checkZenModeRestrictionLocked(int code, int usage, int uid, String packageName) {
Yin-Chia Yeh51d85162019-08-06 15:31:39 -070095 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
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
132 public void setCameraAudioRestriction(@CAMERA_AUDIO_RESTRICTION int mode) {
133 synchronized (this) {
134 mCameraAudioRestriction = mode;
135 }
136 }
137
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
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
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}